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 ItemSerializer::parts(*this);
286}
287
289{
290 int version = 0;
291 QByteArray data;
292 ItemSerializer::serialize(*this, FullPayload, data, version);
293 return data;
294}
295
297{
298 ItemSerializer::deserialize(*this, FullPayload, data, 0, ItemSerializer::Internal);
299}
300
302{
303 d_ptr->mClearPayload = true;
304}
305
306int Item::revision() const
307{
308 return d_ptr->mRevision;
309}
310
311void Item::setRevision(int rev)
312{
313 d_ptr->mRevision = rev;
314}
315
317{
318 return d_ptr->mCollectionId;
319}
320
321void Item::setStorageCollectionId(Collection::Id collectionId)
322{
323 d_ptr->mCollectionId = collectionId;
324}
325
327{
328 return d_ptr->mMimeType;
329}
330
331void Item::setSize(qint64 size)
332{
333 d_ptr->mSize = size;
334 d_ptr->mSizeChanged = true;
335}
336
337qint64 Item::size() const
338{
339 return d_ptr->mSize;
340}
341
342void Item::setMimeType(const QString &mimeType)
343{
344 d_ptr->mMimeType = mimeType;
345}
346
347void Item::setGid(const QString &id)
348{
349 d_ptr->mGid = id;
350}
351
353{
354 return d_ptr->mGid;
355}
356
358{
359 d_ptr->mVirtualReferences = collections;
360}
361
363{
364 return d_ptr->mVirtualReferences;
365}
366
368{
369 return d_ptr->hasMetaTypeId(-1);
370}
371
373{
374 QUrlQuery query;
375 query.addQueryItem(QStringLiteral("item"), QString::number(id()));
376 if (type == UrlWithMimeType) {
377 query.addQueryItem(QStringLiteral("type"), mimeType());
378 }
379
380 QUrl url;
381 url.setScheme(QStringLiteral("akonadi"));
382 url.setQuery(query);
383 return url;
384}
385
387{
388 if (url.scheme() != QLatin1StringView("akonadi")) {
389 return Item();
390 }
391
392 const QString itemStr = QUrlQuery(url).queryItemValue(QStringLiteral("item"));
393 bool ok = false;
394 Item::Id itemId = itemStr.toLongLong(&ok);
395 if (!ok) {
396 return Item();
397 }
398
399 return Item(itemId);
400}
401
402Internal::PayloadBase *Item::payloadBaseV2(int spid, int mtid) const
403{
404 return d_ptr->payloadBaseImpl(spid, mtid);
405}
406
407bool Item::ensureMetaTypeId(int mtid) const
408{
409 // 0. Nothing there - nothing to convert from, either
410 if (d_ptr->mPayloads.empty()) {
411 return false;
412 }
413
414 // 1. Look whether we already have one:
415 if (d_ptr->hasMetaTypeId(mtid)) {
416 return true;
417 }
418
419 // recursion detection (shouldn't trigger, but does if the
420 // serialiser plugins are acting funky):
421 if (d_ptr->mConversionInProgress) {
422 return false;
423 }
424
425 // 2. Try to create one by conversion from a different representation:
426 try {
427 const QScopedValueRollback guard(d_ptr->mConversionInProgress, true);
428 Item converted = ItemSerializer::convert(*this, mtid);
429 return d_ptr->movePayloadFrom(converted.d_ptr, mtid);
430 } catch (const std::exception &e) {
431 qCWarning(AKONADICORE_LOG) << "Item payload conversion threw:" << e.what();
432 return false;
433 } catch (...) {
434 qCCritical(AKONADICORE_LOG, "conversion threw something not derived from std::exception: fix the program!");
435 return false;
436 }
437}
438
439static QString format_type(int spid, int mtid)
440{
441 return QStringLiteral("sp(%1)<%2>").arg(spid).arg(QLatin1StringView(QMetaType(mtid).name()));
442}
443
444static QString format_types(const PayloadContainer &container)
445{
446 QStringList result;
447 result.reserve(container.size());
448 for (auto it = container.begin(), end = container.end(); it != end; ++it) {
449 result.push_back(format_type(it->sharedPointerId, it->metaTypeId));
450 }
451 return result.join(QLatin1StringView(", "));
452}
453
454static QString format_reason(bool valid, Item::Id id)
455{
456 if (valid) {
457 return QStringLiteral("itemId: %1").arg(id);
458 } else {
459 return QStringLiteral("Item is not valid");
460 }
461}
462
463void Item::throwPayloadException(int spid, int mtid) const
464{
465 const auto reason = format_reason(isValid(), id());
466
467 if (d_ptr->mPayloads.empty()) {
468 qCDebug(AKONADICORE_LOG) << "Throwing PayloadException for Item" << id() << ": No payload set";
469 throw PayloadException(QStringLiteral("No Item payload set (%1)").arg(reason));
470 } else {
471 const auto requestedType = format_type(spid, mtid);
472 const auto presentType = format_types(d_ptr->mPayloads);
473 qCDebug(AKONADICORE_LOG) << "Throwing PayloadException for Item" << id() << ": Wrong payload type (requested:" << requestedType
474 << "; present: " << presentType << "), item mime type is" << mimeType();
475 throw PayloadException(QStringLiteral("Wrong Item payload type (requested: %1; present: %2, %3)").arg(requestedType, presentType, reason));
476 }
477}
478
479void Item::setPayloadBaseV2(int spid, int mtid, std::unique_ptr<Internal::PayloadBase> &p)
480{
481 d_ptr->setPayloadBaseImpl(spid, mtid, p, false);
482}
483
484void Item::addPayloadBaseVariant(int spid, int mtid, std::unique_ptr<Internal::PayloadBase> &p) const
485{
486 d_ptr->setPayloadBaseImpl(spid, mtid, p, true);
487}
488
490{
491 return d_ptr->mCachedPayloadParts;
492}
493
494void Item::setCachedPayloadParts(const QSet<QByteArray> &cachedParts)
495{
496 d_ptr->mCachedPayloadParts = cachedParts;
497}
498
500{
501 return ItemSerializer::availableParts(*this);
502}
503
505{
506 QList<int> result;
507 result.reserve(d_ptr->mPayloads.size());
508 // Stable Insertion Sort - N is typically _very_ low (1 or 2).
509 for (auto it = d_ptr->mPayloads.begin(), end = d_ptr->mPayloads.end(); it != end; ++it) {
510 result.insert(std::upper_bound(result.begin(), result.end(), it->metaTypeId), it->metaTypeId);
511 }
512 return result;
513}
514
515void Item::setPayloadPath(const QString &filePath)
516{
517 // Load payload from the external file, so that it's accessible via
518 // Item::payload(). It internally calls setPayload(), which will clear
519 // mPayloadPath, so we call it afterwards
520 ItemSerializer::deserialize(*this, "RFC822", filePath.toUtf8(), 0, ItemSerializer::Foreign);
521 d_ptr->mPayloadPath = filePath;
522}
523
525{
526 return d_ptr->mPayloadPath;
527}
528
529void Item::apply(const Item &other)
530{
531 if (mimeType() != other.mimeType() || id() != other.id()) {
532 qCDebug(AKONADICORE_LOG) << "mimeType() = " << mimeType() << "; other.mimeType() = " << other.mimeType();
533 qCDebug(AKONADICORE_LOG) << "id() = " << id() << "; other.id() = " << other.id();
534 Q_ASSERT_X(false, "Item::apply", "mimetype or id mismatch");
535 }
536
537 setRemoteId(other.remoteId());
538 setRevision(other.revision());
540 setFlags(other.flags());
541 setTags(other.tags());
543 setSize(other.size());
545 setStorageCollectionId(other.storageCollectionId());
546
547 ItemChangeLog *changelog = ItemChangeLog::instance();
548 changelog->attributeStorage(d_ptr) = changelog->attributeStorage(other.d_ptr);
549
550 ItemSerializer::apply(*this, other);
551 d_ptr->resetChangeLog();
552
553 // Must happen after payload update
554 d_ptr->mPayloadPath = other.payloadPath();
555}
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:100
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:362
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:296
QString payloadPath() const
Returns path to the payload file set by setPayloadPath()
Definition item.cpp:524
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:357
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:105
QString gid() const
Returns the gid of the entity.
Definition item.cpp:352
Item()
Creates a new item.
Definition item.cpp:36
qint64 size() const
Returns the size of the items in bytes.
Definition item.cpp:337
QString mimeType() const
Returns the mime type of the item.
Definition item.cpp:326
void setSize(qint64 size)
Set the size of the item in bytes.
Definition item.cpp:331
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:342
QUrl url(UrlType type=UrlShort) const
Returns the url of the item.
Definition item.cpp:372
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:311
QList< int > availablePayloadMetaTypeIds() const
Returns a list of metatype-ids, describing the different variants of payload that are currently conta...
Definition item.cpp:504
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:306
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:515
bool hasAttribute() const
Returns whether the item has an attribute of the requested type.
Definition item.h:748
Collection::Id storageCollectionId() const
Returns the unique identifier of the collection this item is stored in.
Definition item.cpp:316
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:288
static const char FullPayload[]
Describes the part name that is used to fetch the full payload of an item.
Definition item.h:126
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:499
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:728
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:489
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:301
void apply(const Item &other)
Applies the parts of Item other to this item.
Definition item.cpp:529
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:347
QSet< QByteArray > loadedPayloadParts() const
Returns the list of loaded payload parts.
Definition item.cpp:283
void removeAttribute()
Removes and deletes the attribute of the requested type.
Definition item.h:742
UrlType
Describes the type of url which is returned in url().
Definition item.h:602
@ UrlWithMimeType
A url with identifier and mimetype.
Definition item.h:604
static Item fromUrl(const QUrl &url)
Creates an item from the given url.
Definition item.cpp:386
An Akonadi Tag.
Definition tag.h:26
Helper integration between Akonadi and Qt.
QString name(StandardAction 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)
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 Fri Oct 4 2024 11:57:20 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.