Akonadi

protocol.cpp
1/*
2 * SPDX-FileCopyrightText: 2015 Daniel Vrátil <dvratil@redhat.com>
3 * SPDX-FileCopyrightText: 2016 Daniel Vrátil <dvratil@kde.org>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8#include "datastream_p_p.h"
9#include "protocol_p.h"
10#include "scope_p.h"
11
12#include <type_traits>
13#include <typeinfo>
14
15#include <QHash>
16#include <QJsonArray>
17#include <QJsonObject>
18
19#include <cassert>
20
21// clazy:excludeall=function-args-by-value
22
23#undef AKONADI_DECLARE_PRIVATE
24#define AKONADI_DECLARE_PRIVATE(Class) \
25 inline Class##Private *Class::d_func() \
26 { \
27 return reinterpret_cast<Class##Private *>(d_ptr.data()); \
28 } \
29 inline const Class##Private *Class::d_func() const \
30 { \
31 return reinterpret_cast<const Class##Private *>(d_ptr.constData()); \
32 }
33
34namespace Akonadi
35{
36namespace Protocol
37{
38QDebug operator<<(QDebug _dbg, Command::Type type)
39{
40 QDebug dbg(_dbg.noquote());
41
42 switch (type) {
43 case Command::Invalid:
44 return dbg << "Invalid";
45
46 case Command::Hello:
47 return dbg << "Hello";
48 case Command::Login:
49 return dbg << "Login";
50 case Command::Logout:
51 return dbg << "Logout";
52
53 case Command::Transaction:
54 return dbg << "Transaction";
55
56 case Command::CreateItem:
57 return dbg << "CreateItem";
58 case Command::CopyItems:
59 return dbg << "CopyItems";
60 case Command::DeleteItems:
61 return dbg << "DeleteItems";
62 case Command::FetchItems:
63 return dbg << "FetchItems";
64 case Command::LinkItems:
65 return dbg << "LinkItems";
66 case Command::ModifyItems:
67 return dbg << "ModifyItems";
68 case Command::MoveItems:
69 return dbg << "MoveItems";
70
71 case Command::CreateCollection:
72 return dbg << "CreateCollection";
73 case Command::CopyCollection:
74 return dbg << "CopyCollection";
75 case Command::DeleteCollection:
76 return dbg << "DeleteCollection";
77 case Command::FetchCollections:
78 return dbg << "FetchCollections";
79 case Command::FetchCollectionStats:
80 return dbg << "FetchCollectionStats";
81 case Command::ModifyCollection:
82 return dbg << "ModifyCollection";
83 case Command::MoveCollection:
84 return dbg << "MoveCollection";
85
86 case Command::Search:
87 return dbg << "Search";
88 case Command::SearchResult:
89 return dbg << "SearchResult";
90 case Command::StoreSearch:
91 return dbg << "StoreSearch";
92
93 case Command::CreateTag:
94 return dbg << "CreateTag";
95 case Command::DeleteTag:
96 return dbg << "DeleteTag";
97 case Command::FetchTags:
98 return dbg << "FetchTags";
99 case Command::ModifyTag:
100 return dbg << "ModifyTag";
101
102 case Command::SelectResource:
103 return dbg << "SelectResource";
104
105 case Command::StreamPayload:
106 return dbg << "StreamPayload";
107 case Command::ItemChangeNotification:
108 return dbg << "ItemChangeNotification";
109 case Command::CollectionChangeNotification:
110 return dbg << "CollectionChangeNotification";
111 case Command::TagChangeNotification:
112 return dbg << "TagChangeNotification";
113 case Command::SubscriptionChangeNotification:
114 return dbg << "SubscriptionChangeNotification";
115 case Command::DebugChangeNotification:
116 return dbg << "DebugChangeNotification";
117 case Command::CreateSubscription:
118 return dbg << "CreateSubscription";
119 case Command::ModifySubscription:
120 return dbg << "ModifySubscription";
121
122 case Command::_ResponseBit:
123 Q_ASSERT(false);
124 return dbg << static_cast<int>(type);
125 }
126
127 Q_ASSERT(false);
128 return dbg << static_cast<int>(type);
129}
130
131template<typename T>
132DataStream &operator<<(DataStream &stream, const QSharedPointer<T> &ptr)
133{
134 Protocol::serialize(stream, ptr);
135 return stream;
136}
137
138template<typename T>
139DataStream &operator>>(DataStream &stream, QSharedPointer<T> &ptr)
140{
141 ptr = Protocol::deserialize(stream.device()).staticCast<T>();
142 return stream;
143}
144
145/******************************************************************************/
146
147Command::Command(quint8 type)
148 : mType(type)
149{
150}
151
152bool Command::operator==(const Command &other) const
153{
154 return mType == other.mType;
155}
156
157void Command::toJson(QJsonObject &json) const
158{
159 json[QStringLiteral("response")] = static_cast<bool>(mType & Command::_ResponseBit);
160
161#define case_label(x) \
162 case Command::x: { \
163 json[QStringLiteral("type")] = QStringLiteral(#x); \
164 } break;
165
166 // clang-format off
167 switch (mType & ~Command::_ResponseBit) {
168 case_label(Invalid)
169 case_label(Hello)
170 case_label(Login)
171 case_label(Logout)
172
173 case_label(Transaction)
174
175 case_label(CreateItem)
176 case_label(CopyItems)
177 case_label(DeleteItems)
178 case_label(FetchItems)
179 case_label(LinkItems)
180 case_label(ModifyItems)
181 case_label(MoveItems)
182
183 case_label(CreateCollection)
184 case_label(CopyCollection)
185 case_label(DeleteCollection)
186 case_label(FetchCollections)
187 case_label(FetchCollectionStats)
188 case_label(ModifyCollection)
189 case_label(MoveCollection)
190
191 case_label(Search)
192 case_label(SearchResult)
193 case_label(StoreSearch)
194
195 case_label(CreateTag)
196 case_label(DeleteTag)
197 case_label(FetchTags)
198 case_label(ModifyTag)
199
200 case_label(SelectResource)
201
202 case_label(StreamPayload)
203 case_label(CreateSubscription)
204 case_label(ModifySubscription)
205
206 case_label(DebugChangeNotification)
207 case_label(ItemChangeNotification)
208 case_label(CollectionChangeNotification)
209 case_label(TagChangeNotification)
210 case_label(SubscriptionChangeNotification)
211 }
212 // clang-format on
213#undef case_label
214}
215
216DataStream &operator<<(DataStream &stream, const Command &cmd)
217{
218 return stream << cmd.mType;
219}
220
221DataStream &operator>>(DataStream &stream, Command &cmd)
222{
223 return stream >> cmd.mType;
224}
225
226QDebug operator<<(QDebug dbg, const Command &cmd)
227{
228 return dbg.noquote() << ((cmd.mType & Command::_ResponseBit) ? "Response:" : "Command:") << static_cast<Command::Type>(cmd.mType & ~Command::_ResponseBit)
229 << "\n";
230}
231
232void toJson(const Akonadi::Protocol::Command *command, QJsonObject &json)
233{
234#define case_notificationlabel(x, class) \
235 case Command::x: { \
236 static_cast<const Akonadi::Protocol::class *>(command)->toJson(json); \
237 } break;
238#define case_commandlabel(x, cmd, resp) \
239 case Command::x: { \
240 static_cast<const Akonadi::Protocol::cmd *>(command)->toJson(json); \
241 } break; \
242 case Command::x | Command::_ResponseBit: { \
243 static_cast<const Akonadi::Protocol::resp *>(command)->toJson(json); \
244 } break;
245
246 switch (command->mType) {
247 case Command::Invalid:
248 break;
249
250 // clang-format off
251 case Command::Hello | Command::_ResponseBit: {
252 static_cast<const Akonadi::Protocol::HelloResponse *>(command)->toJson(json);
253 } break;
254 case_commandlabel(Login, LoginCommand, LoginResponse)
255 case_commandlabel(Logout, LogoutCommand, LogoutResponse)
256
257 case_commandlabel(Transaction, TransactionCommand, TransactionResponse)
258
259 case_commandlabel(CreateItem, CreateItemCommand, CreateItemResponse)
260 case_commandlabel(CopyItems, CopyItemsCommand, CopyItemsResponse)
261 case_commandlabel(DeleteItems, DeleteItemsCommand, DeleteItemsResponse)
262 case_commandlabel(FetchItems, FetchItemsCommand, FetchItemsResponse)
263 case_commandlabel(LinkItems, LinkItemsCommand, LinkItemsResponse)
264 case_commandlabel(ModifyItems, ModifyItemsCommand, ModifyItemsResponse)
265 case_commandlabel(MoveItems, MoveItemsCommand, MoveItemsResponse)
266
267 case_commandlabel(CreateCollection, CreateCollectionCommand, CreateCollectionResponse)
268 case_commandlabel(CopyCollection, CopyCollectionCommand, CopyCollectionResponse)
269 case_commandlabel(DeleteCollection, DeleteCollectionCommand, DeleteCollectionResponse)
270 case_commandlabel(FetchCollections, FetchCollectionsCommand, FetchCollectionsResponse)
271 case_commandlabel(FetchCollectionStats, FetchCollectionStatsCommand, FetchCollectionStatsResponse)
272 case_commandlabel(ModifyCollection, ModifyCollectionCommand, ModifyCollectionResponse)
273 case_commandlabel(MoveCollection, MoveCollectionCommand, MoveCollectionResponse)
274
275 case_commandlabel(Search, SearchCommand, SearchResponse)
276 case_commandlabel(SearchResult, SearchResultCommand, SearchResultResponse)
277 case_commandlabel(StoreSearch, StoreSearchCommand, StoreSearchResponse)
278
279 case_commandlabel(CreateTag, CreateTagCommand, CreateTagResponse)
280 case_commandlabel(DeleteTag, DeleteTagCommand, DeleteTagResponse)
281 case_commandlabel(FetchTags, FetchTagsCommand, FetchTagsResponse)
282 case_commandlabel(ModifyTag, ModifyTagCommand, ModifyTagResponse)
283
284 case_commandlabel(SelectResource, SelectResourceCommand, SelectResourceResponse)
285
286 case_commandlabel(StreamPayload, StreamPayloadCommand, StreamPayloadResponse)
287
288 case_commandlabel(CreateSubscription, CreateSubscriptionCommand, CreateSubscriptionResponse)
289 case_commandlabel(ModifySubscription, ModifySubscriptionCommand, ModifySubscriptionResponse)
290
291 case_notificationlabel(DebugChangeNotification, DebugChangeNotification)
292 case_notificationlabel(ItemChangeNotification, ItemChangeNotification)
293 case_notificationlabel(CollectionChangeNotification, CollectionChangeNotification)
294 case_notificationlabel(TagChangeNotification, TagChangeNotification)
295 case_notificationlabel(SubscriptionChangeNotification, SubscriptionChangeNotification)
296 }
297 // clang-format on
298#undef case_notificationlabel
299#undef case_commandlabel
300}
301
302/******************************************************************************/
303
304Response::Response()
305 : Command(Command::Invalid | Command::_ResponseBit)
306 , mErrorCode(0)
307{
308}
309
310Response::Response(Command::Type type)
311 : Command(type | Command::_ResponseBit)
312 , mErrorCode(0)
313{
314}
315
316bool Response::operator==(const Response &other) const
317{
318 return *static_cast<const Command *>(this) == static_cast<const Command &>(other) && mErrorCode == other.mErrorCode && mErrorMsg == other.mErrorMsg;
319}
320
321void Response::toJson(QJsonObject &json) const
322{
323 static_cast<const Command *>(this)->toJson(json);
324 if (isError()) {
326 error[QStringLiteral("code")] = errorCode();
327 error[QStringLiteral("message")] = errorMessage();
328 json[QStringLiteral("error")] = error;
329 } else {
330 json[QStringLiteral("error")] = false;
331 }
332}
333
334DataStream &operator<<(DataStream &stream, const Response &cmd)
335{
336 return stream << static_cast<const Command &>(cmd) << cmd.mErrorCode << cmd.mErrorMsg;
337}
338
339DataStream &operator>>(DataStream &stream, Response &cmd)
340{
341 return stream >> static_cast<Command &>(cmd) >> cmd.mErrorCode >> cmd.mErrorMsg;
342}
343
344QDebug operator<<(QDebug dbg, const Response &resp)
345{
346 return dbg.noquote() << static_cast<const Command &>(resp) << "Error code:" << resp.mErrorCode << "\n"
347 << "Error msg:" << resp.mErrorMsg << "\n";
348}
349
350/******************************************************************************/
351
352class FactoryPrivate
353{
354public:
355 typedef CommandPtr (*CommandFactoryFunc)();
356 typedef ResponsePtr (*ResponseFactoryFunc)();
357
358 FactoryPrivate()
359 {
360 // Session management
361 registerType<Command::Hello, Command /* invalid */, HelloResponse>();
362 registerType<Command::Login, LoginCommand, LoginResponse>();
363 registerType<Command::Logout, LogoutCommand, LogoutResponse>();
364
365 // Transactions
366 registerType<Command::Transaction, TransactionCommand, TransactionResponse>();
367
368 // Items
369 registerType<Command::CreateItem, CreateItemCommand, CreateItemResponse>();
370 registerType<Command::CopyItems, CopyItemsCommand, CopyItemsResponse>();
371 registerType<Command::DeleteItems, DeleteItemsCommand, DeleteItemsResponse>();
372 registerType<Command::FetchItems, FetchItemsCommand, FetchItemsResponse>();
373 registerType<Command::LinkItems, LinkItemsCommand, LinkItemsResponse>();
374 registerType<Command::ModifyItems, ModifyItemsCommand, ModifyItemsResponse>();
375 registerType<Command::MoveItems, MoveItemsCommand, MoveItemsResponse>();
376
377 // Collections
378 registerType<Command::CreateCollection, CreateCollectionCommand, CreateCollectionResponse>();
379 registerType<Command::CopyCollection, CopyCollectionCommand, CopyCollectionResponse>();
380 registerType<Command::DeleteCollection, DeleteCollectionCommand, DeleteCollectionResponse>();
381 registerType<Command::FetchCollections, FetchCollectionsCommand, FetchCollectionsResponse>();
382 registerType<Command::FetchCollectionStats, FetchCollectionStatsCommand, FetchCollectionStatsResponse>();
383 registerType<Command::ModifyCollection, ModifyCollectionCommand, ModifyCollectionResponse>();
384 registerType<Command::MoveCollection, MoveCollectionCommand, MoveCollectionResponse>();
385
386 // Search
387 registerType<Command::Search, SearchCommand, SearchResponse>();
388 registerType<Command::SearchResult, SearchResultCommand, SearchResultResponse>();
389 registerType<Command::StoreSearch, StoreSearchCommand, StoreSearchResponse>();
390
391 // Tag
392 registerType<Command::CreateTag, CreateTagCommand, CreateTagResponse>();
393 registerType<Command::DeleteTag, DeleteTagCommand, DeleteTagResponse>();
394 registerType<Command::FetchTags, FetchTagsCommand, FetchTagsResponse>();
395 registerType<Command::ModifyTag, ModifyTagCommand, ModifyTagResponse>();
396
397 // Resources
398 registerType<Command::SelectResource, SelectResourceCommand, SelectResourceResponse>();
399
400 // Other...?
401 registerType<Command::StreamPayload, StreamPayloadCommand, StreamPayloadResponse>();
402 registerType<Command::ItemChangeNotification, ItemChangeNotification, Response /* invalid */>();
403 registerType<Command::CollectionChangeNotification, CollectionChangeNotification, Response /* invalid */>();
404 registerType<Command::TagChangeNotification, TagChangeNotification, Response /* invalid */>();
405 registerType<Command::SubscriptionChangeNotification, SubscriptionChangeNotification, Response /* invalid */>();
406 registerType<Command::DebugChangeNotification, DebugChangeNotification, Response /* invalid */>();
407 registerType<Command::CreateSubscription, CreateSubscriptionCommand, CreateSubscriptionResponse>();
408 registerType<Command::ModifySubscription, ModifySubscriptionCommand, ModifySubscriptionResponse>();
409 }
410
411 // clang has problem resolving the right qHash() overload for Command::Type,
412 // so use its underlying integer type instead
413 QHash<std::underlying_type<Command::Type>::type, QPair<CommandFactoryFunc, ResponseFactoryFunc>> registrar;
414
415private:
416 template<typename T>
417 static CommandPtr commandFactoryFunc()
418 {
420 }
421 template<typename T>
422 static ResponsePtr responseFactoryFunc()
423 {
425 }
426
427 template<Command::Type T, typename CmdType, typename RespType>
428 void registerType()
429 {
430 CommandFactoryFunc cmdFunc = &commandFactoryFunc<CmdType>;
431 ResponseFactoryFunc respFunc = &responseFactoryFunc<RespType>;
432 registrar.insert(T, qMakePair(cmdFunc, respFunc));
433 }
434};
435
436Q_GLOBAL_STATIC(FactoryPrivate, sFactoryPrivate) // NOLINT(readability-redundant-member-init)
437
438CommandPtr Factory::command(Command::Type type)
439{
440 auto iter = sFactoryPrivate->registrar.constFind(type);
441 if (iter == sFactoryPrivate->registrar.constEnd()) {
443 }
444 return iter->first();
445}
446
447ResponsePtr Factory::response(Command::Type type)
448{
449 auto iter = sFactoryPrivate->registrar.constFind(type);
450 if (iter == sFactoryPrivate->registrar.constEnd()) {
452 }
453 return iter->second();
454}
455
456/******************************************************************************/
457
458/******************************************************************************/
459
460bool ItemFetchScope::operator==(const ItemFetchScope &other) const
461{
462 return mRequestedParts == other.mRequestedParts && mChangedSince == other.mChangedSince && mAncestorDepth == other.mAncestorDepth && mFlags == other.mFlags;
463}
464
465QList<QByteArray> ItemFetchScope::requestedPayloads() const
466{
468 std::copy_if(mRequestedParts.begin(), mRequestedParts.end(), std::back_inserter(rv), [](const QByteArray &ba) {
469 return ba.startsWith("PLD:");
470 });
471 return rv;
472}
473
474void ItemFetchScope::setFetch(FetchFlags attributes, bool fetch)
475{
476 if (fetch) {
477 mFlags |= attributes;
478 if (attributes & FullPayload) {
479 if (!mRequestedParts.contains(AKONADI_PARAM_PLD_RFC822)) {
480 mRequestedParts << AKONADI_PARAM_PLD_RFC822;
481 }
482 }
483 } else {
484 mFlags &= ~attributes;
485 }
486}
487
488bool ItemFetchScope::fetch(FetchFlags flags) const
489{
490 if (flags == None) {
491 return mFlags == None;
492 } else {
493 return mFlags & flags;
494 }
495}
496
497void ItemFetchScope::toJson(QJsonObject &json) const
498{
499 json[QStringLiteral("flags")] = static_cast<int>(mFlags);
500 json[QStringLiteral("ChangedSince")] = mChangedSince.toString();
501 json[QStringLiteral("AncestorDepth")] = static_cast<std::underlying_type<AncestorDepth>::type>(mAncestorDepth);
502
503 QJsonArray requestedPartsArray;
504 for (const auto &part : std::as_const(mRequestedParts)) {
505 requestedPartsArray.append(QString::fromUtf8(part));
506 }
507 json[QStringLiteral("RequestedParts")] = requestedPartsArray;
508}
509
510QDebug operator<<(QDebug dbg, ItemFetchScope::AncestorDepth depth)
511{
512 switch (depth) {
513 case ItemFetchScope::NoAncestor:
514 return dbg << "No ancestor";
515 case ItemFetchScope::ParentAncestor:
516 return dbg << "Parent ancestor";
517 case ItemFetchScope::AllAncestors:
518 return dbg << "All ancestors";
519 }
520 Q_UNREACHABLE();
521}
522
523DataStream &operator<<(DataStream &stream, const ItemFetchScope &scope)
524{
525 return stream << scope.mRequestedParts << scope.mChangedSince << scope.mAncestorDepth << scope.mFlags;
526}
527
528DataStream &operator>>(DataStream &stream, ItemFetchScope &scope)
529{
530 return stream >> scope.mRequestedParts >> scope.mChangedSince >> scope.mAncestorDepth >> scope.mFlags;
531}
532
533QDebug operator<<(QDebug dbg, const ItemFetchScope &scope)
534{
535 return dbg.noquote() << "FetchScope(\n"
536 << "Fetch Flags:" << scope.mFlags << "\n"
537 << "Changed Since:" << scope.mChangedSince << "\n"
538 << "Ancestor Depth:" << scope.mAncestorDepth << "\n"
539 << "Requested Parts:" << scope.mRequestedParts << ")\n";
540}
541
542/******************************************************************************/
543
544ScopeContext::ScopeContext(Type type, qint64 id)
545{
546 if (type == ScopeContext::Tag) {
547 mTagCtx = id;
548 } else if (type == ScopeContext::Collection) {
549 mColCtx = id;
550 }
551}
552
553ScopeContext::ScopeContext(Type type, const QString &ctx)
554{
555 if (type == ScopeContext::Tag) {
556 mTagCtx = ctx;
557 } else if (type == ScopeContext::Collection) {
558 mColCtx = ctx;
559 }
560}
561
562bool ScopeContext::operator==(const ScopeContext &other) const
563{
564 return mColCtx == other.mColCtx && mTagCtx == other.mTagCtx;
565}
566
567void ScopeContext::toJson(QJsonObject &json) const
568{
569 if (isEmpty()) {
570 json[QStringLiteral("scopeContext")] = false;
571 } else if (hasContextId(ScopeContext::Tag)) {
572 json[QStringLiteral("scopeContext")] = QStringLiteral("tag");
573 json[QStringLiteral("TagID")] = contextId(ScopeContext::Tag);
574 } else if (hasContextId(ScopeContext::Collection)) {
575 json[QStringLiteral("scopeContext")] = QStringLiteral("collection");
576 json[QStringLiteral("ColID")] = contextId(ScopeContext::Collection);
577 } else if (hasContextRID(ScopeContext::Tag)) {
578 json[QStringLiteral("scopeContext")] = QStringLiteral("tagrid");
579 json[QStringLiteral("TagRID")] = contextRID(ScopeContext::Tag);
580 } else if (hasContextRID(ScopeContext::Collection)) {
581 json[QStringLiteral("scopeContext")] = QStringLiteral("colrid");
582 json[QStringLiteral("ColRID")] = contextRID(ScopeContext::Collection);
583 }
584}
585
586DataStream &operator<<(DataStream &stream, const ScopeContext &context)
587{
588 // We don't have a custom generic DataStream streaming operator for QVariant
589 // because it's very hard, esp. without access to QVariant private
590 // stuff, so we have to decompose it manually here.
591 auto vType = context.mColCtx.typeId();
592 stream << vType;
593 if (vType == QMetaType::LongLong) {
594 stream << context.mColCtx.toLongLong();
595 } else if (vType == QMetaType::QString) {
596 stream << context.mColCtx.toString();
597 }
598
599 vType = context.mTagCtx.typeId();
600 stream << vType;
601 if (vType == QMetaType::LongLong) {
602 stream << context.mTagCtx.toLongLong();
603 } else if (vType == QMetaType::QString) {
604 stream << context.mTagCtx.toString();
605 }
606
607 return stream;
608}
609
610DataStream &operator>>(DataStream &stream, ScopeContext &context)
611{
612 int vType;
613 qint64 id;
614 QString rid;
615
616 for (ScopeContext::Type type : {ScopeContext::Collection, ScopeContext::Tag}) {
617 stream >> vType;
618 if (vType == QMetaType::LongLong) {
619 stream >> id;
620 context.setContext(type, id);
621 } else if (vType == QMetaType::QString) {
622 stream >> rid;
623 context.setContext(type, rid);
624 }
625 }
626
627 return stream;
628}
629
630QDebug operator<<(QDebug _dbg, const ScopeContext &ctx)
631{
632 QDebug dbg(_dbg.noquote());
633 dbg << "ScopeContext(";
634 if (ctx.isEmpty()) {
635 dbg << "empty";
636 } else if (ctx.hasContextId(ScopeContext::Tag)) {
637 dbg << "Tag ID:" << ctx.contextId(ScopeContext::Tag);
638 } else if (ctx.hasContextId(ScopeContext::Collection)) {
639 dbg << "Col ID:" << ctx.contextId(ScopeContext::Collection);
640 } else if (ctx.hasContextRID(ScopeContext::Tag)) {
641 dbg << "Tag RID:" << ctx.contextRID(ScopeContext::Tag);
642 } else if (ctx.hasContextRID(ScopeContext::Collection)) {
643 dbg << "Col RID:" << ctx.contextRID(ScopeContext::Collection);
644 }
645 return dbg << ")\n";
646}
647
648/******************************************************************************/
649
650ChangeNotification::ChangeNotification(Command::Type type)
651 : Command(type)
652{
653}
654
655bool ChangeNotification::operator==(const ChangeNotification &other) const
656{
657 return static_cast<const Command &>(*this) == other && mSessionId == other.mSessionId;
658 // metadata are not compared
659}
660
661QList<qint64> ChangeNotification::itemsToUids(const QList<FetchItemsResponse> &items)
662{
663 QList<qint64> rv;
664 rv.reserve(items.size());
665 std::transform(items.cbegin(), items.cend(), std::back_inserter(rv), [](const FetchItemsResponse &item) {
666 return item.id();
667 });
668 return rv;
669}
670
671bool ChangeNotification::isRemove() const
672{
673 switch (type()) {
674 case Command::Invalid:
675 return false;
676 case Command::ItemChangeNotification:
677 return static_cast<const class ItemChangeNotification *>(this)->operation() == ItemChangeNotification::Remove;
678 case Command::CollectionChangeNotification:
679 return static_cast<const class CollectionChangeNotification *>(this)->operation() == CollectionChangeNotification::Remove;
680 case Command::TagChangeNotification:
681 return static_cast<const class TagChangeNotification *>(this)->operation() == TagChangeNotification::Remove;
682 case Command::SubscriptionChangeNotification:
683 return static_cast<const class SubscriptionChangeNotification *>(this)->operation() == SubscriptionChangeNotification::Remove;
684 case Command::DebugChangeNotification:
685 return false;
686 default:
687 Q_ASSERT_X(false, __FUNCTION__, "Unknown ChangeNotification type");
688 }
689
690 return false;
691}
692
693bool ChangeNotification::isMove() const
694{
695 switch (type()) {
696 case Command::Invalid:
697 return false;
698 case Command::ItemChangeNotification:
699 return static_cast<const class ItemChangeNotification *>(this)->operation() == ItemChangeNotification::Move;
700 case Command::CollectionChangeNotification:
701 return static_cast<const class CollectionChangeNotification *>(this)->operation() == CollectionChangeNotification::Move;
702 case Command::TagChangeNotification:
703 case Command::SubscriptionChangeNotification:
704 case Command::DebugChangeNotification:
705 return false;
706 default:
707 Q_ASSERT_X(false, __FUNCTION__, "Unknown ChangeNotification type");
708 }
709
710 return false;
711}
712
713bool ChangeNotification::appendAndCompress(ChangeNotificationList &list, const ChangeNotificationPtr &msg)
714{
715 // It is likely that compressible notifications are within the last few notifications, so avoid searching a list that is potentially huge
716 static const int maxCompressionSearchLength = 10;
717 int searchCounter = 0;
718 // There are often multiple Collection Modify notifications in the queue,
719 // so we optimize for this case.
720
721 if (msg->type() == Command::CollectionChangeNotification) {
722 const auto &cmsg = Protocol::cmdCast<class CollectionChangeNotification>(msg);
723 if (cmsg.operation() == CollectionChangeNotification::Modify) {
724 // We are iterating from end, since there's higher probability of finding
725 // matching notification
726 for (auto iter = list.end(), begin = list.begin(); iter != begin;) {
727 --iter;
728 if ((*iter)->type() == Protocol::Command::CollectionChangeNotification) {
729 auto &it = Protocol::cmdCast<class CollectionChangeNotification>(*iter);
730 const auto &msgCol = cmsg.collection();
731 const auto &itCol = it.collection();
732 if (msgCol.id() == itCol.id() && msgCol.remoteId() == itCol.remoteId() && msgCol.remoteRevision() == itCol.remoteRevision()
733 && msgCol.resource() == itCol.resource() && cmsg.destinationResource() == it.destinationResource()
734 && cmsg.parentCollection() == it.parentCollection() && cmsg.parentDestCollection() == it.parentDestCollection()) {
735 // both are modifications, merge them together and drop the new one
736 if (cmsg.operation() == CollectionChangeNotification::Modify && it.operation() == CollectionChangeNotification::Modify) {
737 const auto parts = it.changedParts();
738 it.setChangedParts(parts + cmsg.changedParts());
739 return false;
740 }
741
742 // we found Add notification, which means we can drop this modification
743 if (it.operation() == CollectionChangeNotification::Add) {
744 return false;
745 }
746 }
747 }
748 searchCounter++;
749 if (searchCounter >= maxCompressionSearchLength) {
750 break;
751 }
752 }
753 }
754 }
755
756 // All other cases are just append, as the compression becomes too expensive in large batches
757 list.append(msg);
758 return true;
759}
760
761void ChangeNotification::toJson(QJsonObject &json) const
762{
763 static_cast<const Command *>(this)->toJson(json);
764 json[QStringLiteral("session")] = QString::fromUtf8(mSessionId);
765
766 QJsonArray metadata;
767 for (const auto &m : std::as_const(mMetaData)) {
768 metadata.append(QString::fromUtf8(m));
769 }
770 json[QStringLiteral("metadata")] = metadata;
771}
772
773DataStream &operator<<(DataStream &stream, const ChangeNotification &ntf)
774{
775 return stream << static_cast<const Command &>(ntf) << ntf.mSessionId;
776}
777
778DataStream &operator>>(DataStream &stream, ChangeNotification &ntf)
779{
780 return stream >> static_cast<Command &>(ntf) >> ntf.mSessionId;
781}
782
783QDebug operator<<(QDebug dbg, const ChangeNotification &ntf)
784{
785 return dbg.noquote() << static_cast<const Command &>(ntf) << "Session:" << ntf.mSessionId << "\n"
786 << "MetaData:" << ntf.mMetaData << "\n";
787}
788
789} // namespace Protocol
790} // namespace Akonadi
791
792// Helpers for the generated code
793namespace Akonadi
794{
795namespace Protocol
796{
797template<typename Value, template<typename> class Container>
798inline bool containerComparator(const Container<Value> &c1, const Container<Value> &c2)
799{
800 return c1 == c2;
801}
802
803template<typename T, template<typename> class Container>
804inline bool containerComparator(const Container<QSharedPointer<T>> &c1, const Container<QSharedPointer<T>> &c2)
805{
806 if (c1.size() != c2.size()) {
807 return false;
808 }
809
810 for (auto it1 = c1.cbegin(), it2 = c2.cbegin(), end1 = c1.cend(); it1 != end1; ++it1, ++it2) {
811 if (**it1 != **it2) {
812 return false;
813 }
814 }
815 return true;
816}
817
818} // namespace Protocol
819} // namespace Akonadi
820
821/******************************************************************************/
822
823// Here comes the generated protocol implementation
824#include "protocol_gen.cpp"
825
826/******************************************************************************/
Helper integration between Akonadi and Qt.
KCALUTILS_EXPORT QString errorMessage(const KCalendarCore::Exception &exception)
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QDebug operator<<(QDebug dbg, const PerceptualColor::MultiSpinBoxSection &value)
QDebug & noquote()
iterator insert(const Key &key, const T &value)
void append(const QJsonValue &value)
void append(QList< T > &&value)
iterator begin()
const_iterator cbegin() const const
const_iterator cend() const const
iterator end()
void reserve(qsizetype size)
qsizetype size() const const
QSharedPointer< T > create(Args &&... args)
QSharedPointer< X > staticCast() const const
QString fromUtf8(QByteArrayView str)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:08:30 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.