Akonadi

item.cpp
1 /*
2  SPDX-FileCopyrightText: 2006 Volker Krause <[email protected]>
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 
24 using namespace Akonadi;
25 
26 Q_GLOBAL_STATIC(Akonadi::Collection, s_defaultParentCollection) // NOLINT(readability-redundant-member-init)
27 
28 uint Akonadi::qHash(const Akonadi::Item &item)
29 {
30  return ::qHash(item.id());
31 }
32 
33 // Change to something != RFC822 as soon as the server supports it
34 const char Item::FullPayload[] = "RFC822";
35 
37  : d_ptr(new ItemPrivate)
38 {
39 }
40 
42  : d_ptr(new ItemPrivate(id))
43 {
44 }
45 
46 Item::Item(const QString &mimeType)
47  : d_ptr(new ItemPrivate)
48 {
49  d_ptr->mMimeType = mimeType;
50 }
51 
52 Item::Item(const Item &other) = default;
53 
54 Item::Item(Item &&other) noexcept = default;
55 
56 Item::~Item() = default;
57 
58 void Item::setId(Item::Id identifier)
59 {
60  d_ptr->mId = identifier;
61 }
62 
64 {
65  return d_ptr->mId;
66 }
67 
68 void Item::setRemoteId(const QString &id)
69 {
70  d_ptr->mRemoteId = id;
71 }
72 
74 {
75  return d_ptr->mRemoteId;
76 }
77 
78 void Item::setRemoteRevision(const QString &revision)
79 {
80  d_ptr->mRemoteRevision = revision;
81 }
82 
84 {
85  return d_ptr->mRemoteRevision;
86 }
87 
88 bool Item::isValid() const
89 {
90  return (d_ptr->mId >= 0);
91 }
92 
93 bool 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 
99 bool Akonadi::Item::operator!=(const Item &other) const
100 {
101  return (isValid() || other.isValid()) && (d_ptr->mId != other.d_ptr->mId);
102 }
103 
104 Item &Item ::operator=(const Item &other)
105 {
106  if (this != &other) {
107  d_ptr = other.d_ptr;
108  }
109 
110  return *this;
111 }
112 
113 bool 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 
124 {
125  ItemChangeLog::instance()->attributeStorage(d_ptr).removeAttribute(type);
126 }
127 
128 bool 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 
148 const 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 
180 void 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 
194 void Item::clearFlag(const QByteArray &name)
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 
208 void 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 
230 bool Item::hasFlag(const QByteArray &name) const
231 {
232  return d_ptr->mFlags.contains(name);
233 }
234 
235 void Item::setTags(const Tag::List &list)
236 {
237  d_ptr->mTags = list;
238  d_ptr->mTagsOverwritten = true;
239 }
240 
241 void 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 
254 void Item::clearTags()
255 {
256  d_ptr->mTags.clear();
257  d_ptr->mTagsOverwritten = true;
258 }
259 
260 void 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 
273 bool Item::hasTag(const Tag &tag) const
274 {
275  return d_ptr->mTags.contains(tag);
276 }
277 
278 Tag::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 
311 int Item::revision() const
312 {
313  return d_ptr->mRevision;
314 }
315 
316 void Item::setRevision(int rev)
317 {
318  d_ptr->mRevision = rev;
319 }
320 
322 {
323  return d_ptr->mCollectionId;
324 }
325 
326 void Item::setStorageCollectionId(Collection::Id collectionId)
327 {
328  d_ptr->mCollectionId = collectionId;
329 }
330 
332 {
333  return d_ptr->mMimeType;
334 }
335 
336 void Item::setSize(qint64 size)
337 {
338  d_ptr->mSize = size;
339  d_ptr->mSizeChanged = true;
340 }
341 
342 qint64 Item::size() const
343 {
344  return d_ptr->mSize;
345 }
346 
347 void Item::setMimeType(const QString &mimeType)
348 {
349  d_ptr->mMimeType = mimeType;
350 }
351 
352 void 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 
372 bool Item::hasPayload() const
373 {
374  return d_ptr->hasMetaTypeId(-1);
375 }
376 
377 QUrl Item::url(UrlType type) const
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() != QLatin1String("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 
407 Internal::PayloadBase *Item::payloadBaseV2(int spid, int mtid) const
408 {
409  return d_ptr->payloadBaseImpl(spid, mtid);
410 }
411 
412 bool 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 
444 static QString format_type(int spid, int mtid)
445 {
446  return QStringLiteral("sp(%1)<%2>").arg(spid).arg(QLatin1String(QMetaType(mtid).name()));
447 }
448 
449 static 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(QLatin1String(", "));
457 }
458 
459 static 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 
468 void 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 
484 void Item::setPayloadBaseV2(int spid, int mtid, std::unique_ptr<Internal::PayloadBase> &p)
485 {
486  d_ptr->setPayloadBaseImpl(spid, mtid, p, false);
487 }
488 
489 void 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 
499 void 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 
520 void 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 
534 void 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 }
bool isValid() const
Returns whether the item is valid.
Definition: item.cpp:88
@ UrlWithMimeType
A url with identifier and mimetype.
Definition: item.h:612
void setSize(qint64 size)
Set the size of the item in bytes.
Definition: item.cpp:336
void setRevision(int revision)
Sets the revision number of the item.
Definition: item.cpp:316
const T * attribute() const
Returns the attribute of the requested type or 0 if it is not available.
Definition: item.h:736
Collection::List virtualReferences() const
Lists virtual collections that this item is linked to.
Definition: item.cpp:367
bool hasFlag(const QByteArray &name) const
Returns whether the flag with the given name is set in the item.
Definition: item.cpp:230
QString number(int n, int base)
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
QSet::iterator erase(QSet::iterator pos)
UrlType
Describes the type of url which is returned in url().
Definition: item.h:610
Flags flags() const
Returns all flags of this item.
Definition: item.cpp:175
QString scheme() const const
static const char FullPayload[]
Describes the part name that is used to fetch the full payload of an item.
Definition: item.h:127
An Akonadi Tag.
Definition: tag.h:25
QSet::iterator find(const T &value)
qlonglong toLongLong(bool *ok, int base) const const
QDateTime modificationTime() const
Returns the timestamp of the last modification of this item.
Definition: item.cpp:220
void setParentCollection(const Collection &parent)
Set the parent collection of this object.
Definition: item.cpp:170
Represents a collection of PIM items.
Definition: collection.h:61
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 setVirtualReferences(const Collection::List &collections)
Sets the virtual collections that this item is linked into.
Definition: item.cpp:362
void setMimeType(const QString &mimeType)
Sets the mime type of the item to mimeType.
Definition: item.cpp:347
void push_back(const T &value)
void clearAttributes()
Removes and deletes all attributes of the item.
Definition: item.cpp:138
Provides interface for custom attributes for Entity.
Definition: attribute.h:131
void setGid(const QString &gid)
Sets the gid of the entity.
Definition: item.cpp:352
int revision() const
Returns the revision number of the item.
Definition: item.cpp:311
bool contains(const T &value) const const
Definition: item.h:29
bool hasAttribute() const
Returns whether the item has an attribute of the requested type.
Definition: item.h:756
bool hasPayload() const
Returns whether the item has a payload object.
Definition: item.cpp:372
QList< int > availablePayloadMetaTypeIds() const
Returns a list of metatype-ids, describing the different variants of payload that are currently conta...
Definition: item.cpp:509
Q_GLOBAL_STATIC(Internal::StaticControl, s_instance) class ControlPrivate
Definition: control.cpp:28
void setScheme(const QString &scheme)
~Item()
Destroys the item.
qint64 size() const
Returns the size of the items in bytes.
Definition: item.cpp:342
bool operator==(const Item &other) const
Returns whether this item's id equals the id of the other item.
Definition: item.cpp:93
void reserve(int alloc)
qint64 Id
Describes the unique id type.
Definition: item.h:106
QString mimeType() const
Returns the mime type of the item.
Definition: item.cpp:331
Attribute::List attributes() const
Returns a list of all attributes of the item.
Definition: item.cpp:133
QUrl url(UrlType type=UrlShort) const
Returns the url of the item.
Definition: item.cpp:377
void setModificationTime(const QDateTime &datetime)
Sets the timestamp of the last modification of this item.
Definition: item.cpp:225
Collection::Id storageCollectionId() const
Returns the unique identifier of the collection this item is stored in.
Definition: item.cpp:321
QString remoteRevision() const
Returns the remote revision of the item.
Definition: item.cpp:83
QString gid() const
Returns the gid of the entity.
Definition: item.cpp:357
static Item fromUrl(const QUrl &url)
Creates an item from the given url.
Definition: item.cpp:391
bool removeOne(const T &value)
QByteArray toUtf8() const const
Collection parentCollection() const
Returns the parent collection of this object.
Definition: item.cpp:153
void setRemoteId(const QString &id)
Sets the remote id of the item.
Definition: item.cpp:68
Item & operator=(const Item &other)
Assigns the other to this item and returns a reference to this item.
Definition: item.cpp:104
QString join(const QString &separator) const const
void removeAttribute()
Removes and deletes the attribute of the requested type.
Definition: item.h:750
QString payloadPath() const
Returns path to the payload file set by setPayloadPath()
Definition: item.cpp:529
void setPayloadPath(const QString &filePath)
Sets a path to a file with full payload.
Definition: item.cpp:520
void insert(int i, const T &value)
void setQuery(const QString &query, QUrl::ParsingMode mode)
void setRemoteRevision(const QString &revision)
Sets the remote revision of the item.
Definition: item.cpp:78
Id id() const
Returns the unique identifier of the item.
Definition: item.cpp:63
Relation::List relations() const
Returns all relations of this item.
Definition: item.cpp:283
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
bool operator<(const Item &other) const
Definition: item.cpp:113
void addAttribute(Attribute *attribute)
Adds an attribute to the item.
Definition: item.cpp:118
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
QSet< QByteArray > availablePayloadParts() const
Returns the parts available for this item.
Definition: item.cpp:504
const char * name(StandardAction id)
QSet::iterator end()
void clearFlags()
Removes all flags from the item.
Definition: item.cpp:214
Item()
Creates a new item.
Definition: item.cpp:36
QList::iterator begin()
QSet< QByteArray > cachedPayloadParts() const
Returns the parts available for this item in the cache.
Definition: item.cpp:494
QByteArray payloadData() const
Returns the full payload in its canonical representation, e.g.
Definition: item.cpp:293
QString queryItemValue(const QString &key, QUrl::ComponentFormattingOptions encoding) const const
void setFlags(const Flags &flags)
Overwrites all flags of the item by the given flags.
Definition: item.cpp:208
QSet< QByteArray > loadedPayloadParts() const
Returns the list of loaded payload parts.
Definition: item.cpp:288
QList::iterator end()
void apply(const Item &other)
Applies the parts of Item other to this item.
Definition: item.cpp:534
void clearFlag(const QByteArray &name)
Removes the flag with the given name from the item.
Definition: item.cpp:194
void setFlag(const QByteArray &name)
Sets the flag with the given name in the item.
Definition: item.cpp:180
void setId(Id identifier)
Sets the unique identifier of the item.
Definition: item.cpp:58
qint64 Id
Describes the unique id type.
Definition: collection.h:79
QString remoteId() const
Returns the remote id of the item.
Definition: item.cpp:73
Represents a PIM item stored in Akonadi storage.
Definition: item.h:100
Helper integration between Akonadi and Qt.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Oct 1 2023 04:01:48 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.