mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-24 19:02:51 +00:00
611 lines
14 KiB
C++
611 lines
14 KiB
C++
![]() |
/*
|
||
|
Copyright (c) 2006 Volker Krause <vkrause@kde.org>
|
||
|
|
||
|
This library is free software; you can redistribute it and/or modify it
|
||
|
under the terms of the GNU Library General Public License as published by
|
||
|
the Free Software Foundation; either version 2 of the License, or (at your
|
||
|
option) any later version.
|
||
|
|
||
|
This library is distributed in the hope that it will be useful, but WITHOUT
|
||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
|
||
|
License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Library General Public License
|
||
|
along with this library; see the file COPYING.LIB. If not, write to the
|
||
|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||
|
02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
#include "item.h"
|
||
|
#include "item_p.h"
|
||
|
#include "itemserializer_p.h"
|
||
|
#include "protocol_p.h"
|
||
|
|
||
|
#include <kurl.h>
|
||
|
#include <kdebug.h>
|
||
|
|
||
|
#include <QtCore/QStringList>
|
||
|
#include <QtCore/QReadWriteLock>
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <map>
|
||
|
#include <utility>
|
||
|
|
||
|
using namespace Akonadi;
|
||
|
using namespace boost;
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
struct nodelete {
|
||
|
template <typename T>
|
||
|
void operator()(T *)
|
||
|
{
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct ByTypeId {
|
||
|
typedef bool result_type;
|
||
|
bool operator()(const boost::shared_ptr<PayloadBase> &lhs, const boost::shared_ptr<PayloadBase> &rhs) const
|
||
|
{
|
||
|
return strcmp(lhs->typeName(), rhs->typeName()) < 0 ;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
} // anon namespace
|
||
|
|
||
|
typedef QHash< QString, std::map< boost::shared_ptr<PayloadBase>, std::pair<int, int>, ByTypeId > > LegacyMap;
|
||
|
Q_GLOBAL_STATIC(LegacyMap, typeInfoToMetaTypeIdMap)
|
||
|
Q_GLOBAL_STATIC_WITH_ARGS(QReadWriteLock, legacyMapLock, (QReadWriteLock::Recursive))
|
||
|
|
||
|
void Item::addToLegacyMappingImpl(const QString &mimeType, int spid, int mtid, std::auto_ptr<PayloadBase> p)
|
||
|
{
|
||
|
if (!p.get()) {
|
||
|
return;
|
||
|
}
|
||
|
const boost::shared_ptr<PayloadBase> sp(p);
|
||
|
const QWriteLocker locker(legacyMapLock());
|
||
|
std::pair<int, int> &item = (*typeInfoToMetaTypeIdMap())[mimeType][sp];
|
||
|
item.first = spid;
|
||
|
item.second = mtid;
|
||
|
}
|
||
|
|
||
|
namespace {
|
||
|
class MyReadLocker {
|
||
|
public:
|
||
|
explicit MyReadLocker(QReadWriteLock *rwl)
|
||
|
: rwl(rwl)
|
||
|
, locked(false)
|
||
|
{
|
||
|
if (rwl) {
|
||
|
rwl->lockForRead();
|
||
|
}
|
||
|
locked = true;
|
||
|
}
|
||
|
|
||
|
~MyReadLocker()
|
||
|
{
|
||
|
if (rwl && locked) {
|
||
|
rwl->unlock();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
shared_ptr<T> makeUnlockingPointer(T *t)
|
||
|
{
|
||
|
if (t) {
|
||
|
// the bind() doesn't throw, so if shared_ptr
|
||
|
// construction line below, or anything else after it,
|
||
|
// throws, we're unlocked. Mark us as such:
|
||
|
locked = false;
|
||
|
const shared_ptr<T> result(t, bind(&QReadWriteLock::unlock, rwl));
|
||
|
// from now on, the shared_ptr is responsible for unlocking
|
||
|
return result;
|
||
|
} else {
|
||
|
return shared_ptr<T>();
|
||
|
}
|
||
|
}
|
||
|
private:
|
||
|
QReadWriteLock *const rwl;
|
||
|
bool locked;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
static shared_ptr<const std::pair<int, int> > lookupLegacyMapping(const QString &mimeType, PayloadBase *p)
|
||
|
{
|
||
|
MyReadLocker locker(legacyMapLock());
|
||
|
const LegacyMap::const_iterator hit = typeInfoToMetaTypeIdMap()->constFind(mimeType);
|
||
|
if (hit == typeInfoToMetaTypeIdMap()->constEnd()) {
|
||
|
return shared_ptr<const std::pair<int, int> >();
|
||
|
}
|
||
|
const boost::shared_ptr<PayloadBase> sp(p, nodelete());
|
||
|
const LegacyMap::mapped_type::const_iterator it = hit->find(sp);
|
||
|
if (it == hit->end()) {
|
||
|
return shared_ptr<const std::pair<int, int> >();
|
||
|
}
|
||
|
|
||
|
return locker.makeUnlockingPointer(&it->second);
|
||
|
}
|
||
|
|
||
|
// Change to something != RFC822 as soon as the server supports it
|
||
|
const char *Item::FullPayload = "RFC822";
|
||
|
|
||
|
Item::Item()
|
||
|
: Entity(new ItemPrivate)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
Item::Item(Id id)
|
||
|
: Entity(new ItemPrivate(id))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
Item::Item(const QString &mimeType)
|
||
|
: Entity(new ItemPrivate)
|
||
|
{
|
||
|
d_func()->mMimeType = mimeType;
|
||
|
}
|
||
|
|
||
|
Item::Item(const Item &other)
|
||
|
: Entity(other)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
Item::~Item()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
Item::Flags Item::flags() const
|
||
|
{
|
||
|
return d_func()->mFlags;
|
||
|
}
|
||
|
|
||
|
void Item::setFlag(const QByteArray &name)
|
||
|
{
|
||
|
Q_D(Item);
|
||
|
d->mFlags.insert(name);
|
||
|
if (!d->mFlagsOverwritten) {
|
||
|
if (d->mDeletedFlags.contains(name)) {
|
||
|
d->mDeletedFlags.remove(name);
|
||
|
} else {
|
||
|
d->mAddedFlags.insert(name);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Item::clearFlag(const QByteArray &name)
|
||
|
{
|
||
|
Q_D(Item);
|
||
|
d->mFlags.remove(name);
|
||
|
if (!d->mFlagsOverwritten) {
|
||
|
if (d->mAddedFlags.contains(name)) {
|
||
|
d->mAddedFlags.remove(name);
|
||
|
} else {
|
||
|
d->mDeletedFlags.insert(name);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Item::setFlags(const Flags &flags)
|
||
|
{
|
||
|
Q_D(Item);
|
||
|
d->mFlags = flags;
|
||
|
d->mFlagsOverwritten = true;
|
||
|
}
|
||
|
|
||
|
void Item::clearFlags()
|
||
|
{
|
||
|
Q_D(Item);
|
||
|
d->mFlags.clear();
|
||
|
d->mFlagsOverwritten = true;
|
||
|
}
|
||
|
|
||
|
QDateTime Item::modificationTime() const
|
||
|
{
|
||
|
return d_func()->mModificationTime;
|
||
|
}
|
||
|
|
||
|
void Item::setModificationTime(const QDateTime &datetime)
|
||
|
{
|
||
|
d_func()->mModificationTime = datetime;
|
||
|
}
|
||
|
|
||
|
bool Item::hasFlag(const QByteArray &name) const
|
||
|
{
|
||
|
return d_func()->mFlags.contains(name);
|
||
|
}
|
||
|
|
||
|
void Item::setTags(const Tag::List &list)
|
||
|
{
|
||
|
Q_D(Item);
|
||
|
d->mTags = list;
|
||
|
d->mTagsOverwritten = true;
|
||
|
}
|
||
|
|
||
|
void Item::setTag(const Tag &tag)
|
||
|
{
|
||
|
Q_D(Item);
|
||
|
d->mTags << tag;
|
||
|
if (!d->mTagsOverwritten) {
|
||
|
if (d->mDeletedTags.contains(tag)) {
|
||
|
d->mDeletedTags.removeOne(tag);
|
||
|
} else {
|
||
|
d->mAddedTags << tag ;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Item::clearTags()
|
||
|
{
|
||
|
Q_D(Item);
|
||
|
d->mTags.clear();
|
||
|
d->mTagsOverwritten = true;
|
||
|
}
|
||
|
|
||
|
void Item::clearTag(const Tag &tag)
|
||
|
{
|
||
|
Q_D(Item);
|
||
|
d->mTags.removeOne(tag);
|
||
|
if (!d->mTagsOverwritten) {
|
||
|
if (d->mAddedTags.contains(tag)) {
|
||
|
d->mAddedTags.removeOne(tag);
|
||
|
} else {
|
||
|
d->mDeletedTags << tag;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool Item::hasTag(const Tag &tag) const
|
||
|
{
|
||
|
Q_D(const Item);
|
||
|
return d->mTags.contains(tag);
|
||
|
}
|
||
|
|
||
|
Tag::List Item::tags() const
|
||
|
{
|
||
|
Q_D(const Item);
|
||
|
return d->mTags;
|
||
|
}
|
||
|
|
||
|
QSet<QByteArray> Item::loadedPayloadParts() const
|
||
|
{
|
||
|
return ItemSerializer::parts(*this);
|
||
|
}
|
||
|
|
||
|
QByteArray Item::payloadData() const
|
||
|
{
|
||
|
int version = 0;
|
||
|
QByteArray data;
|
||
|
ItemSerializer::serialize(*this, FullPayload, data, version);
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
void Item::setPayloadFromData(const QByteArray &data)
|
||
|
{
|
||
|
ItemSerializer::deserialize(*this, FullPayload, data, 0, false);
|
||
|
}
|
||
|
|
||
|
void Item::clearPayload()
|
||
|
{
|
||
|
d_func()->mClearPayload = true;
|
||
|
}
|
||
|
|
||
|
int Item::revision() const
|
||
|
{
|
||
|
return d_func()->mRevision;
|
||
|
}
|
||
|
|
||
|
void Item::setRevision(int rev)
|
||
|
{
|
||
|
d_func()->mRevision = rev;
|
||
|
}
|
||
|
|
||
|
Entity::Id Item::storageCollectionId() const
|
||
|
{
|
||
|
return d_func()->mCollectionId;
|
||
|
}
|
||
|
|
||
|
void Item::setStorageCollectionId(Entity::Id collectionId)
|
||
|
{
|
||
|
d_func()->mCollectionId = collectionId;
|
||
|
}
|
||
|
|
||
|
QString Item::mimeType() const
|
||
|
{
|
||
|
return d_func()->mMimeType;
|
||
|
}
|
||
|
|
||
|
void Item::setSize(qint64 size)
|
||
|
{
|
||
|
Q_D(Item);
|
||
|
d->mSize = size;
|
||
|
d->mSizeChanged = true;
|
||
|
}
|
||
|
|
||
|
qint64 Item::size() const
|
||
|
{
|
||
|
return d_func()->mSize;
|
||
|
}
|
||
|
|
||
|
void Item::setMimeType(const QString &mimeType)
|
||
|
{
|
||
|
d_func()->mMimeType = mimeType;
|
||
|
}
|
||
|
|
||
|
void Item::setGid(const QString &id)
|
||
|
{
|
||
|
d_func()->mGid = id;
|
||
|
}
|
||
|
|
||
|
QString Item::gid() const
|
||
|
{
|
||
|
return d_func()->mGid;
|
||
|
}
|
||
|
|
||
|
void Item::setVirtualReferences(const Collection::List &collections)
|
||
|
{
|
||
|
d_func()->mVirtualReferences = collections;
|
||
|
}
|
||
|
|
||
|
Collection::List Item::virtualReferences() const
|
||
|
{
|
||
|
return d_func()->mVirtualReferences;
|
||
|
}
|
||
|
|
||
|
bool Item::hasPayload() const
|
||
|
{
|
||
|
return d_func()->hasMetaTypeId(-1);
|
||
|
}
|
||
|
|
||
|
KUrl Item::url(UrlType type) const
|
||
|
{
|
||
|
KUrl url;
|
||
|
url.setProtocol(QString::fromLatin1("akonadi"));
|
||
|
url.addQueryItem(QLatin1String("item"), QString::number(id()));
|
||
|
|
||
|
if (type == UrlWithMimeType) {
|
||
|
url.addQueryItem(QLatin1String("type"), mimeType());
|
||
|
}
|
||
|
|
||
|
return url;
|
||
|
}
|
||
|
|
||
|
Item Item::fromUrl(const KUrl &url)
|
||
|
{
|
||
|
if (url.protocol() != QLatin1String("akonadi")) {
|
||
|
return Item();
|
||
|
}
|
||
|
|
||
|
const QString itemStr = url.queryItem(QLatin1String("item"));
|
||
|
bool ok = false;
|
||
|
Item::Id itemId = itemStr.toLongLong(&ok);
|
||
|
if (!ok) {
|
||
|
return Item();
|
||
|
}
|
||
|
|
||
|
return Item(itemId);
|
||
|
}
|
||
|
|
||
|
namespace {
|
||
|
class Dummy
|
||
|
{
|
||
|
};
|
||
|
}
|
||
|
|
||
|
Q_GLOBAL_STATIC(Payload<Dummy>, dummyPayload)
|
||
|
|
||
|
PayloadBase *Item::payloadBase() const
|
||
|
{
|
||
|
Q_D(const Item);
|
||
|
d->tryEnsureLegacyPayload();
|
||
|
if (d->mLegacyPayload) {
|
||
|
return d->mLegacyPayload.get();
|
||
|
} else {
|
||
|
return dummyPayload();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ItemPrivate::tryEnsureLegacyPayload() const
|
||
|
{
|
||
|
if (!mLegacyPayload) {
|
||
|
for (PayloadContainer::const_iterator it = mPayloads.begin(), end = mPayloads.end() ; it != end ; ++it) {
|
||
|
if (lookupLegacyMapping(mMimeType, it->payload.get())) {
|
||
|
mLegacyPayload = it->payload; // clones
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PayloadBase *Item::payloadBaseV2(int spid, int mtid) const
|
||
|
{
|
||
|
return d_func()->payloadBaseImpl(spid, mtid);
|
||
|
}
|
||
|
|
||
|
namespace {
|
||
|
class ConversionGuard
|
||
|
{
|
||
|
const bool old;
|
||
|
bool &b;
|
||
|
public:
|
||
|
explicit ConversionGuard(bool &b)
|
||
|
: old(b)
|
||
|
, b(b)
|
||
|
{
|
||
|
b = true;
|
||
|
}
|
||
|
~ConversionGuard() {
|
||
|
b = old;
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
bool Item::ensureMetaTypeId(int mtid) const
|
||
|
{
|
||
|
Q_D(const Item);
|
||
|
// 0. Nothing there - nothing to convert from, either
|
||
|
if (d->mPayloads.empty()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// 1. Look whether we already have one:
|
||
|
if (d->hasMetaTypeId(mtid)) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// recursion detection (shouldn't trigger, but does if the
|
||
|
// serialiser plugins are acting funky):
|
||
|
if (d->mConversionInProgress) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// 2. Try to create one by conversion from a different representation:
|
||
|
try {
|
||
|
const ConversionGuard guard(d->mConversionInProgress);
|
||
|
Item converted = ItemSerializer::convert(*this, mtid);
|
||
|
return d->movePayloadFrom(converted.d_func(), mtid);
|
||
|
} catch (const std::exception &e) {
|
||
|
kDebug() << "conversion threw:" << e.what();
|
||
|
return false;
|
||
|
} catch (...) {
|
||
|
kDebug() << "conversion threw something not derived from std::exception: fix the program!";
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static QString format_type(int spid, int mtid)
|
||
|
{
|
||
|
return QString::fromLatin1("sp(%1)<%2>")
|
||
|
.arg(spid).arg(QLatin1String(QMetaType::typeName(mtid)));
|
||
|
}
|
||
|
|
||
|
static QString format_types(const PayloadContainer &c)
|
||
|
{
|
||
|
QStringList result;
|
||
|
for (PayloadContainer::const_iterator it = c.begin(), end = c.end() ; it != end ; ++it) {
|
||
|
result.push_back(format_type(it->sharedPointerId, it->metaTypeId));
|
||
|
}
|
||
|
return result.join(QLatin1String(", "));
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
QString Item::payloadExceptionText(int spid, int mtid) const
|
||
|
{
|
||
|
Q_D(const Item);
|
||
|
if (d->mPayloads.empty()) {
|
||
|
return QLatin1String("No payload set");
|
||
|
} else {
|
||
|
return QString::fromLatin1("Wrong payload type (requested: %1; present: %2")
|
||
|
.arg(format_type(spid, mtid), format_types(d->mPayloads));
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
void Item::throwPayloadException(int spid, int mtid) const
|
||
|
{
|
||
|
Q_D(const Item);
|
||
|
if (d->mPayloads.empty()) {
|
||
|
throw PayloadException("No payload set");
|
||
|
} else {
|
||
|
throw PayloadException(QString::fromLatin1("Wrong payload type (requested: %1; present: %2")
|
||
|
.arg(format_type(spid, mtid), format_types(d->mPayloads)));
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void Item::setPayloadBase(PayloadBase *p)
|
||
|
{
|
||
|
d_func()->setLegacyPayloadBaseImpl(std::auto_ptr<PayloadBase>(p));
|
||
|
}
|
||
|
|
||
|
void ItemPrivate::setLegacyPayloadBaseImpl(std::auto_ptr<PayloadBase> p)
|
||
|
{
|
||
|
if (const shared_ptr<const std::pair<int, int> > pair = lookupLegacyMapping(mMimeType, p.get())) {
|
||
|
std::auto_ptr<PayloadBase> clone;
|
||
|
if (p.get()) {
|
||
|
clone.reset(p->clone());
|
||
|
}
|
||
|
setPayloadBaseImpl(pair->first, pair->second, p, false);
|
||
|
mLegacyPayload.reset(clone.release());
|
||
|
} else {
|
||
|
mPayloads.clear();
|
||
|
mLegacyPayload.reset(p.release());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Item::setPayloadBaseV2(int spid, int mtid, std::auto_ptr<PayloadBase> p)
|
||
|
{
|
||
|
d_func()->setPayloadBaseImpl(spid, mtid, p, false);
|
||
|
}
|
||
|
|
||
|
void Item::addPayloadBaseVariant(int spid, int mtid, std::auto_ptr<PayloadBase> p) const
|
||
|
{
|
||
|
d_func()->setPayloadBaseImpl(spid, mtid, p, true);
|
||
|
}
|
||
|
|
||
|
QSet<QByteArray> Item::cachedPayloadParts() const
|
||
|
{
|
||
|
Q_D(const Item);
|
||
|
return d->mCachedPayloadParts;
|
||
|
}
|
||
|
|
||
|
void Item::setCachedPayloadParts(const QSet< QByteArray > &cachedParts)
|
||
|
{
|
||
|
Q_D(Item);
|
||
|
d->mCachedPayloadParts = cachedParts;
|
||
|
}
|
||
|
|
||
|
QSet<QByteArray> Item::availablePayloadParts() const
|
||
|
{
|
||
|
return ItemSerializer::availableParts(*this);
|
||
|
}
|
||
|
|
||
|
QVector<int> Item::availablePayloadMetaTypeIds() const
|
||
|
{
|
||
|
QVector<int> result;
|
||
|
Q_D(const Item);
|
||
|
result.reserve(d->mPayloads.size());
|
||
|
// Stable Insertion Sort - N is typically _very_ low (1 or 2).
|
||
|
for (PayloadContainer::const_iterator it = d->mPayloads.begin(), end = d->mPayloads.end() ; it != end ; ++it) {
|
||
|
result.insert(std::upper_bound(result.begin(), result.end(), it->metaTypeId), it->metaTypeId);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void Item::apply(const Item &other)
|
||
|
{
|
||
|
if (mimeType() != other.mimeType() || id() != other.id()) {
|
||
|
kDebug() << "mimeType() = " << mimeType() << "; other.mimeType() = " << other.mimeType();
|
||
|
kDebug() << "id() = " << id() << "; other.id() = " << other.id();
|
||
|
Q_ASSERT_X(false, "Item::apply", "mimetype or id missmatch");
|
||
|
}
|
||
|
|
||
|
setRemoteId(other.remoteId());
|
||
|
setRevision(other.revision());
|
||
|
setRemoteRevision(other.remoteRevision());
|
||
|
setFlags(other.flags());
|
||
|
setTags(other.tags());
|
||
|
setModificationTime(other.modificationTime());
|
||
|
setSize(other.size());
|
||
|
setParentCollection(other.parentCollection());
|
||
|
setStorageCollectionId(other.storageCollectionId());
|
||
|
|
||
|
QList<QByteArray> attrs;
|
||
|
foreach (Attribute *attribute, other.attributes()) {
|
||
|
addAttribute(attribute->clone());
|
||
|
attrs.append(attribute->type());
|
||
|
}
|
||
|
|
||
|
QMutableHashIterator<QByteArray, Attribute *> it(d_ptr->mAttributes);
|
||
|
while (it.hasNext()) {
|
||
|
it.next();
|
||
|
if (!attrs.contains(it.key())) {
|
||
|
delete it.value();
|
||
|
it.remove();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ItemSerializer::apply(*this, other);
|
||
|
d_func()->resetChangeLog();
|
||
|
}
|
||
|
|
||
|
AKONADI_DEFINE_PRIVATE(Item)
|