Akonadi

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

KDE's Doxygen guidelines are available online.