Akonadi

item.cpp
1/*
2 SPDX-FileCopyrightText: 2006 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "item.h"
8#include "akonadicore_debug.h"
9#include "item_p.h"
10#include "itemserializer_p.h"
11#include "private/protocol_p.h"
12
13#include <QUrl>
14#include <QUrlQuery>
15
16#include <QReadWriteLock>
17#include <QScopedValueRollback>
18#include <QStringList>
19
20#include <algorithm>
21#include <map>
22#include <utility>
23
24using namespace Akonadi;
25
26Q_GLOBAL_STATIC(Akonadi::Collection, s_defaultParentCollection) // NOLINT(readability-redundant-member-init)
27
28size_t Akonadi::qHash(const Akonadi::Item &item, size_t seed) noexcept
29{
30 return ::qHash(item.id(), seed);
31}
32
33// Change to something != RFC822 as soon as the server supports it
34const char Item::FullPayload[] = "RFC822";
35
36Item::Item()
37 : d_ptr(new ItemPrivate)
38{
39}
40
42 : d_ptr(new ItemPrivate(id))
43{
44}
45
46Item::Item(const QString &mimeType)
47 : d_ptr(new ItemPrivate)
48{
49 d_ptr->mMimeType = mimeType;
50}
51
52Item::Item(const Item &other) = default;
53
54Item::Item(Item &&other) noexcept = default;
55
56Item::~Item() = default;
57
58void Item::setId(Item::Id identifier)
59{
60 d_ptr->mId = identifier;
61}
62
64{
65 return d_ptr->mId;
66}
67
69{
70 d_ptr->mRemoteId = id;
71}
72
74{
75 return d_ptr->mRemoteId;
76}
77
78void Item::setRemoteRevision(const QString &revision)
79{
80 d_ptr->mRemoteRevision = revision;
81}
82
84{
85 return d_ptr->mRemoteRevision;
86}
87
88bool Item::isValid() const
89{
90 return (d_ptr->mId >= 0);
91}
92
93bool Item::operator==(const Item &other) const
94{
95 // Invalid collections are the same, no matter what their internal ID is
96 return (!isValid() && !other.isValid()) || (d_ptr->mId == other.d_ptr->mId);
97}
98
99bool Akonadi::Item::operator!=(const Item &other) const
100{
101 return (isValid() || other.isValid()) && (d_ptr->mId != other.d_ptr->mId);
102}
103
104Item &Item ::operator=(const Item &other)
105{
106 if (this != &other) {
107 d_ptr = other.d_ptr;
108 }
109
110 return *this;
111}
112
113bool Akonadi::Item::operator<(const Item &other) const
114{
115 return d_ptr->mId < other.d_ptr->mId;
116}
117
119{
120 ItemChangeLog::instance()->attributeStorage(d_ptr).addAttribute(attr);
121}
122
123void Item::removeAttribute(const QByteArray &type)
124{
125 ItemChangeLog::instance()->attributeStorage(d_ptr).removeAttribute(type);
126}
127
128bool Item::hasAttribute(const QByteArray &type) const
129{
130 return ItemChangeLog::instance()->attributeStorage(d_ptr).hasAttribute(type);
131}
132
134{
135 return ItemChangeLog::instance()->attributeStorage(d_ptr).attributes();
136}
137
139{
140 ItemChangeLog::instance()->attributeStorage(d_ptr).clearAttributes();
141}
142
144{
145 return ItemChangeLog::instance()->attributeStorage(d_ptr).attribute(type);
146}
147
148const Attribute *Item::attribute(const QByteArray &type) const
149{
150 return ItemChangeLog::instance()->attributeStorage(d_ptr).attribute(type);
151}
152
154{
155 if (!d_ptr->mParent) {
156 d_ptr->mParent.reset(new Collection());
157 }
158 return *(d_ptr->mParent);
159}
160
162{
163 if (!d_ptr->mParent) {
164 return *(s_defaultParentCollection);
165 } else {
166 return *(d_ptr->mParent);
167 }
168}
169
171{
172 d_ptr->mParent.reset(new Collection(parent));
173}
174
176{
177 return d_ptr->mFlags;
178}
179
180void Item::setFlag(const QByteArray &name)
181{
182 d_ptr->mFlags.insert(name);
183 if (!d_ptr->mFlagsOverwritten) {
184 Item::Flags &deletedFlags = ItemChangeLog::instance()->deletedFlags(d_ptr);
185 auto iter = deletedFlags.find(name);
186 if (iter != deletedFlags.end()) {
187 deletedFlags.erase(iter);
188 } else {
189 ItemChangeLog::instance()->addedFlags(d_ptr).insert(name);
190 }
191 }
192}
193
195{
196 d_ptr->mFlags.remove(name);
197 if (!d_ptr->mFlagsOverwritten) {
198 Item::Flags &addedFlags = ItemChangeLog::instance()->addedFlags(d_ptr);
199 auto iter = addedFlags.find(name);
200 if (iter != addedFlags.end()) {
201 addedFlags.erase(iter);
202 } else {
203 ItemChangeLog::instance()->deletedFlags(d_ptr).insert(name);
204 }
205 }
206}
207
208void Item::setFlags(const Flags &flags)
209{
210 d_ptr->mFlags = flags;
211 d_ptr->mFlagsOverwritten = true;
212}
213
215{
216 d_ptr->mFlags.clear();
217 d_ptr->mFlagsOverwritten = true;
218}
219
221{
222 return d_ptr->mModificationTime;
223}
224
226{
227 d_ptr->mModificationTime = datetime;
228}
229
230bool Item::hasFlag(const QByteArray &name) const
231{
232 return d_ptr->mFlags.contains(name);
233}
234
235void Item::setTags(const Tag::List &list)
236{
237 d_ptr->mTags = list;
238 d_ptr->mTagsOverwritten = true;
239}
240
241void Item::setTag(const Tag &tag)
242{
243 d_ptr->mTags << tag;
244 if (!d_ptr->mTagsOverwritten) {
245 Tag::List &deletedTags = ItemChangeLog::instance()->deletedTags(d_ptr);
246 if (deletedTags.contains(tag)) {
247 deletedTags.removeOne(tag);
248 } else {
249 ItemChangeLog::instance()->addedTags(d_ptr).push_back(tag);
250 }
251 }
252}
253
254void Item::clearTags()
255{
256 d_ptr->mTags.clear();
257 d_ptr->mTagsOverwritten = true;
258}
259
260void Item::clearTag(const Tag &tag)
261{
262 d_ptr->mTags.removeOne(tag);
263 if (!d_ptr->mTagsOverwritten) {
264 Tag::List &addedTags = ItemChangeLog::instance()->addedTags(d_ptr);
265 if (addedTags.contains(tag)) {
266 addedTags.removeOne(tag);
267 } else {
268 ItemChangeLog::instance()->deletedTags(d_ptr).push_back(tag);
269 }
270 }
271}
272
273bool Item::hasTag(const Tag &tag) const
274{
275 return d_ptr->mTags.contains(tag);
276}
277
278Tag::List Item::tags() const
279{
280 return d_ptr->mTags;
281}
282
284{
285 return d_ptr->mRelations;
286}
287
289{
290 return ItemSerializer::parts(*this);
291}
292
294{
295 int version = 0;
296 QByteArray data;
297 ItemSerializer::serialize(*this, FullPayload, data, version);
298 return data;
299}
300
302{
303 ItemSerializer::deserialize(*this, FullPayload, data, 0, ItemSerializer::Internal);
304}
305
307{
308 d_ptr->mClearPayload = true;
309}
310
311int Item::revision() const
312{
313 return d_ptr->mRevision;
314}
315
316void Item::setRevision(int rev)
317{
318 d_ptr->mRevision = rev;
319}
320
322{
323 return d_ptr->mCollectionId;
324}
325
327{
328 d_ptr->mCollectionId = collectionId;
329}
330
332{
333 return d_ptr->mMimeType;
334}
335
336void Item::setSize(qint64 size)
337{
338 d_ptr->mSize = size;
339 d_ptr->mSizeChanged = true;
340}
341
342qint64 Item::size() const
343{
344 return d_ptr->mSize;
345}
346
347void Item::setMimeType(const QString &mimeType)
348{
349 d_ptr->mMimeType = mimeType;
350}
351
352void Item::setGid(const QString &id)
353{
354 d_ptr->mGid = id;
355}
356
358{
359 return d_ptr->mGid;
360}
361
363{
364 d_ptr->mVirtualReferences = collections;
365}
366
368{
369 return d_ptr->mVirtualReferences;
370}
371
373{
374 return d_ptr->hasMetaTypeId(-1);
375}
376
378{
379 QUrlQuery query;
380 query.addQueryItem(QStringLiteral("item"), QString::number(id()));
381 if (type == UrlWithMimeType) {
382 query.addQueryItem(QStringLiteral("type"), mimeType());
383 }
384
385 QUrl url;
386 url.setScheme(QStringLiteral("akonadi"));
387 url.setQuery(query);
388 return url;
389}
390
392{
393 if (url.scheme() != QLatin1StringView("akonadi")) {
394 return Item();
395 }
396
397 const QString itemStr = QUrlQuery(url).queryItemValue(QStringLiteral("item"));
398 bool ok = false;
399 Item::Id itemId = itemStr.toLongLong(&ok);
400 if (!ok) {
401 return Item();
402 }
403
404 return Item(itemId);
405}
406
407Internal::PayloadBase *Item::payloadBaseV2(int spid, int mtid) const
408{
409 return d_ptr->payloadBaseImpl(spid, mtid);
410}
411
412bool Item::ensureMetaTypeId(int mtid) const
413{
414 // 0. Nothing there - nothing to convert from, either
415 if (d_ptr->mPayloads.empty()) {
416 return false;
417 }
418
419 // 1. Look whether we already have one:
420 if (d_ptr->hasMetaTypeId(mtid)) {
421 return true;
422 }
423
424 // recursion detection (shouldn't trigger, but does if the
425 // serialiser plugins are acting funky):
426 if (d_ptr->mConversionInProgress) {
427 return false;
428 }
429
430 // 2. Try to create one by conversion from a different representation:
431 try {
432 const QScopedValueRollback guard(d_ptr->mConversionInProgress, true);
433 Item converted = ItemSerializer::convert(*this, mtid);
434 return d_ptr->movePayloadFrom(converted.d_ptr, mtid);
435 } catch (const std::exception &e) {
436 qCWarning(AKONADICORE_LOG) << "Item payload conversion threw:" << e.what();
437 return false;
438 } catch (...) {
439 qCCritical(AKONADICORE_LOG, "conversion threw something not derived from std::exception: fix the program!");
440 return false;
441 }
442}
443
444static QString format_type(int spid, int mtid)
445{
446 return QStringLiteral("sp(%1)<%2>").arg(spid).arg(QLatin1StringView(QMetaType(mtid).name()));
447}
448
449static QString format_types(const PayloadContainer &container)
450{
451 QStringList result;
452 result.reserve(container.size());
453 for (auto it = container.begin(), end = container.end(); it != end; ++it) {
454 result.push_back(format_type(it->sharedPointerId, it->metaTypeId));
455 }
456 return result.join(QLatin1StringView(", "));
457}
458
459static QString format_reason(bool valid, Item::Id id)
460{
461 if (valid) {
462 return QStringLiteral("itemId: %1").arg(id);
463 } else {
464 return QStringLiteral("Item is not valid");
465 }
466}
467
468void Item::throwPayloadException(int spid, int mtid) const
469{
470 const auto reason = format_reason(isValid(), id());
471
472 if (d_ptr->mPayloads.empty()) {
473 qCDebug(AKONADICORE_LOG) << "Throwing PayloadException for Item" << id() << ": No payload set";
474 throw PayloadException(QStringLiteral("No Item payload set (%1)").arg(reason));
475 } else {
476 const auto requestedType = format_type(spid, mtid);
477 const auto presentType = format_types(d_ptr->mPayloads);
478 qCDebug(AKONADICORE_LOG) << "Throwing PayloadException for Item" << id() << ": Wrong payload type (requested:" << requestedType
479 << "; present: " << presentType << "), item mime type is" << mimeType();
480 throw PayloadException(QStringLiteral("Wrong Item payload type (requested: %1; present: %2, %3)").arg(requestedType, presentType, reason));
481 }
482}
483
484void Item::setPayloadBaseV2(int spid, int mtid, std::unique_ptr<Internal::PayloadBase> &p)
485{
486 d_ptr->setPayloadBaseImpl(spid, mtid, p, false);
487}
488
489void Item::addPayloadBaseVariant(int spid, int mtid, std::unique_ptr<Internal::PayloadBase> &p) const
490{
491 d_ptr->setPayloadBaseImpl(spid, mtid, p, true);
492}
493
495{
496 return d_ptr->mCachedPayloadParts;
497}
498
499void Item::setCachedPayloadParts(const QSet<QByteArray> &cachedParts)
500{
501 d_ptr->mCachedPayloadParts = cachedParts;
502}
503
505{
506 return ItemSerializer::availableParts(*this);
507}
508
510{
511 QList<int> result;
512 result.reserve(d_ptr->mPayloads.size());
513 // Stable Insertion Sort - N is typically _very_ low (1 or 2).
514 for (auto it = d_ptr->mPayloads.begin(), end = d_ptr->mPayloads.end(); it != end; ++it) {
515 result.insert(std::upper_bound(result.begin(), result.end(), it->metaTypeId), it->metaTypeId);
516 }
517 return result;
518}
519
520void Item::setPayloadPath(const QString &filePath)
521{
522 // Load payload from the external file, so that it's accessible via
523 // Item::payload(). It internally calls setPayload(), which will clear
524 // mPayloadPath, so we call it afterwards
525 ItemSerializer::deserialize(*this, "RFC822", filePath.toUtf8(), 0, ItemSerializer::Foreign);
526 d_ptr->mPayloadPath = filePath;
527}
528
530{
531 return d_ptr->mPayloadPath;
532}
533
534void Item::apply(const Item &other)
535{
536 if (mimeType() != other.mimeType() || id() != other.id()) {
537 qCDebug(AKONADICORE_LOG) << "mimeType() = " << mimeType() << "; other.mimeType() = " << other.mimeType();
538 qCDebug(AKONADICORE_LOG) << "id() = " << id() << "; other.id() = " << other.id();
539 Q_ASSERT_X(false, "Item::apply", "mimetype or id mismatch");
540 }
541
542 setRemoteId(other.remoteId());
543 setRevision(other.revision());
545 setFlags(other.flags());
546 setTags(other.tags());
548 setSize(other.size());
550 setStorageCollectionId(other.storageCollectionId());
551
552 ItemChangeLog *changelog = ItemChangeLog::instance();
553 changelog->attributeStorage(d_ptr) = changelog->attributeStorage(other.d_ptr);
554
555 ItemSerializer::apply(*this, other);
556 d_ptr->resetChangeLog();
557
558 // Must happen after payload update
559 d_ptr->mPayloadPath = other.payloadPath();
560}
Provides interface for custom attributes for Entity.
Definition attribute.h:132
Represents a collection of PIM items.
Definition collection.h:62
qint64 Id
Describes the unique id type.
Definition collection.h:79
Represents a PIM item stored in Akonadi storage.
Definition item.h:101
void setParentCollection(const Collection &parent)
Set the parent collection of this object.
Definition item.cpp:170
Collection::List virtualReferences() const
Lists virtual collections that this item is linked to.
Definition item.cpp:367
bool operator<(const Item &other) const
Definition item.cpp:113
bool hasFlag(const QByteArray &name) const
Returns whether the flag with the given name is set in the item.
Definition item.cpp:230
~Item()
Destroys the item.
void setPayloadFromData(const QByteArray &data)
Sets the payload based on the canonical representation normally used for data of this mime type.
Definition item.cpp:301
QString payloadPath() const
Returns path to the payload file set by setPayloadPath()
Definition item.cpp:529
void setId(Id identifier)
Sets the unique identifier of the item.
Definition item.cpp:58
void setVirtualReferences(const Collection::List &collections)
Sets the virtual collections that this item is linked into.
Definition item.cpp:362
void clearFlag(const QByteArray &name)
Removes the flag with the given name from the item.
Definition item.cpp:194
QString remoteRevision() const
Returns the remote revision of the item.
Definition item.cpp:83
qint64 Id
Describes the unique id type.
Definition item.h:106
QString gid() const
Returns the gid of the entity.
Definition item.cpp:357
Item()
Creates a new item.
Definition item.cpp:36
qint64 size() const
Returns the size of the items in bytes.
Definition item.cpp:342
QString mimeType() const
Returns the mime type of the item.
Definition item.cpp:331
void setSize(qint64 size)
Set the size of the item in bytes.
Definition item.cpp:336
void clearFlags()
Removes all flags from the item.
Definition item.cpp:214
void setMimeType(const QString &mimeType)
Sets the mime type of the item to mimeType.
Definition item.cpp:347
QUrl url(UrlType type=UrlShort) const
Returns the url of the item.
Definition item.cpp:377
Relation::List relations() const
Returns all relations of this item.
Definition item.cpp:283
Flags flags() const
Returns all flags of this item.
Definition item.cpp:175
void setRevision(int revision)
Sets the revision number of the item.
Definition item.cpp:316
QList< int > availablePayloadMetaTypeIds() const
Returns a list of metatype-ids, describing the different variants of payload that are currently conta...
Definition item.cpp:509
bool operator!=(const Item &other) const
Returns whether the item's id does not equal the id of the other item.
Definition item.cpp:99
void setModificationTime(const QDateTime &datetime)
Sets the timestamp of the last modification of this item.
Definition item.cpp:225
bool hasPayload() const
Returns whether the item has a payload object.
Id id() const
Returns the unique identifier of the item.
Definition item.cpp:63
bool operator==(const Item &other) const
Returns whether this item's id equals the id of the other item.
Definition item.cpp:93
void clearAttributes()
Removes and deletes all attributes of the item.
Definition item.cpp:138
int revision() const
Returns the revision number of the item.
Definition item.cpp:311
Attribute::List attributes() const
Returns a list of all attributes of the item.
Definition item.cpp:133
void setPayloadPath(const QString &filePath)
Sets a path to a file with full payload.
Definition item.cpp:520
bool hasAttribute() const
Returns whether the item has an attribute of the requested type.
Definition item.h:756
Collection::Id storageCollectionId() const
Returns the unique identifier of the collection this item is stored in.
Definition item.cpp:321
void setRemoteId(const QString &id)
Sets the remote id of the item.
Definition item.cpp:68
QString remoteId() const
Returns the remote id of the item.
Definition item.cpp:73
QByteArray payloadData() const
Returns the full payload in its canonical representation, e.g.
Definition item.cpp:293
static const char FullPayload[]
Describes the part name that is used to fetch the full payload of an item.
Definition item.h:127
bool isValid() const
Returns whether the item is valid.
Definition item.cpp:88
QSet< QByteArray > availablePayloadParts() const
Returns the parts available for this item.
Definition item.cpp:504
Collection parentCollection() const
Returns the parent collection of this object.
Definition item.cpp:161
void setFlag(const QByteArray &name)
Sets the flag with the given name in the item.
Definition item.cpp:180
const T * attribute() const
Returns the attribute of the requested type or 0 if it is not available.
Definition item.h:736
void setFlags(const Flags &flags)
Overwrites all flags of the item by the given flags.
Definition item.cpp:208
void addAttribute(Attribute *attribute)
Adds an attribute to the item.
Definition item.cpp:118
QSet< QByteArray > cachedPayloadParts() const
Returns the parts available for this item in the cache.
Definition item.cpp:494
QDateTime modificationTime() const
Returns the timestamp of the last modification of this item.
Definition item.cpp:220
void clearPayload()
Marks that the payload shall be cleared from the cache when this item is passed to an ItemModifyJob t...
Definition item.cpp:306
void apply(const Item &other)
Applies the parts of Item other to this item.
Definition item.cpp:534
void setRemoteRevision(const QString &revision)
Sets the remote revision of the item.
Definition item.cpp:78
void setGid(const QString &gid)
Sets the gid of the entity.
Definition item.cpp:352
QSet< QByteArray > loadedPayloadParts() const
Returns the list of loaded payload parts.
Definition item.cpp:288
void removeAttribute()
Removes and deletes the attribute of the requested type.
Definition item.h:750
UrlType
Describes the type of url which is returned in url().
Definition item.h:610
@ UrlWithMimeType
A url with identifier and mimetype.
Definition item.h:612
static Item fromUrl(const QUrl &url)
Creates an item from the given url.
Definition item.cpp:391
An Akonadi Tag.
Definition tag.h:26
Helper integration between Akonadi and Qt.
const QList< QKeySequence > & end()
QString name(StandardShortcut id)
iterator begin()
bool contains(const AT &value) const const
iterator end()
iterator insert(const_iterator before, parameter_type value)
void push_back(parameter_type value)
bool removeOne(const AT &t)
void reserve(qsizetype size)
T qobject_cast(QObject *object)
void clear()
iterator end()
iterator erase(const_iterator pos)
iterator find(const T &value)
iterator insert(const T &value)
QString arg(Args &&... args) const const
QString number(double n, char format, int precision)
qlonglong toLongLong(bool *ok, int base) const const
QByteArray toUtf8() const const
QString join(QChar separator) const const
QString scheme() const const
void setQuery(const QString &query, ParsingMode mode)
void setScheme(const QString &scheme)
QString queryItemValue(const QString &key, QUrl::ComponentFormattingOptions encoding) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:38 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.