diff --git a/akonadi/item.cpp b/akonadi/item.cpp index 64e593a53..b459e3bc7 100644 --- a/akonadi/item.cpp +++ b/akonadi/item.cpp @@ -1,212 +1,228 @@ /* Copyright (c) 2006 Volker Krause 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 #include using namespace Akonadi; // 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 ) d->mAddedFlags.insert( name ); } void Item::clearFlag( const QByteArray & name ) { Q_D( Item ); d->mFlags.remove( name ); if ( !d->mFlagsOverwritten ) 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 ); } QSet 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 ); } 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; } bool Item::hasPayload() const { return d_func()->mPayload != 0; } 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 ); } PayloadBase* Item::payloadBase() const { return d_func()->mPayload; } void Item::setPayloadBase( PayloadBase* p ) { Q_D( Item ); delete d->mPayload; d->mPayload = p; } +QSet Item::availablePayloadParts() const +{ + return ItemSerializer::availableParts( *this ); +} + +void Item::merge(const Item &other) +{ + foreach(Attribute *attribute, other.attributes()) + { + addAttribute(attribute->clone()); + } + + ItemSerializer::merge( *this, other ); +} + + AKONADI_DEFINE_PRIVATE( Item ) diff --git a/akonadi/item.h b/akonadi/item.h index 0f4b50cfb..d7d1e4a2b 100644 --- a/akonadi/item.h +++ b/akonadi/item.h @@ -1,417 +1,439 @@ /* Copyright (c) 2006 Volker Krause 2007 Till Adam 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. */ #ifndef AKONADI_ITEM_H #define AKONADI_ITEM_H #include "akonadi_export.h" #include #include #include "itempayloadinternals_p.h" #include #include #include #include #include #include class KUrl; namespace std { template class auto_ptr; } namespace Akonadi { class ItemPrivate; /** * @short Represents a PIM item stored in Akonadi storage. * * A PIM item consists of one or more parts, allowing a fine-grained access on its * content where needed (eg. mail envelope, mail body and attachments). * * There is also a namespace (prefix) for special parts which are local to Akonadi. * These parts, prefixed by "akonadi-" will never be fetched in the resource. * They are useful for local extensions like agents which might want to add meta data * to items in order to handle them but the meta data should not be stored back to the * resource. * * This class contains beside some type-agnostic information (flags, revision) * a single payload object representing its actual data. Which objects these actually * are depends on the mimetype of the item and the corresponding serializer plugin. * * This class is implicitly shared. * *

Payload

* * Technically the only restriction on payload objects is that they have to be copyable. * For safety reasons, pointer payloads are forbidden as well though, as the * ownership would not be clear. In this case, usage of a shared pointer is * recommended (such as boost::shared_ptr or QSharedPointer). * * Using a shared pointer is also recommended in case the payload uses polymorphic * types. For supported shared pointer types implicit casting is provided when possible. * * When using a value-based class as payload, it is recommended to use one that does * support implicit sharing as setting and retrieving a payload as well as copying * an Akonadi::Item object imply copying of the payload object. * * The availability of a payload of a specific type can be checked using hasPayload(), * payloads can be retrieved by using payload() and set by using setPayload(). Refer * to the documentation of those methods for more details. * * @author Volker Krause , Till Adam */ class AKONADI_EXPORT Item : public Entity { public: /** * Describes a list of items. */ typedef QList List; /** * Describes a flag name. */ typedef QByteArray Flag; /** * Describes a set of flag names. */ typedef QSet Flags; /** * Describes the part name that is used to fetch the * full payload of an item. */ static const char* FullPayload; /** * Creates a new item. */ Item(); /** * Creates a new item with the given unique @p id. */ explicit Item( Id id ); /** * Creates a new item with the given mime type. * * @param mimeType The mime type of the item. */ explicit Item( const QString &mimeType ); /** * Creates a new item from an @p other item. */ Item( const Item &other ); /** * Destroys the item. */ ~Item(); /** * Creates an item from the given @p url. */ static Item fromUrl( const KUrl &url ); /** * Returns all flags of this item. */ Flags flags() const; /** * Returns the timestamp of the last modification of this item. * @since 4.2 */ QDateTime modificationTime() const; /** * Sets the timestamp of the last modification of this item. * * @note Do not modify this value from within an application, * it is updated automatically by the revision checking functions. * @since 4.2 */ void setModificationTime( const QDateTime &datetime ); /** * Returns whether the flag with the given @p name is * set in the item. */ bool hasFlag( const QByteArray &name ) const; /** * Sets the flag with the given @p name in the item. */ void setFlag( const QByteArray &name ); /** * Removes the flag with the given @p name from the item. */ void clearFlag( const QByteArray &name ); /** * Overwrites all flags of the item by the given @p flags. */ void setFlags( const Flags &flags ); /** * Removes all flags from the item. */ void clearFlags(); /** * Sets the payload based on the canonical representation normally * used for data of this mime type. * * @param data The encoded data. * @see fullPayloadData */ void setPayloadFromData( const QByteArray &data ); /** * Returns the full payload in its canonical representation, e.g. the * binary or textual format usually used for data with this mime type. * This is useful when communicating with non-Akonadi application by * e.g. drag&drop, copy&paste or stored files. */ QByteArray payloadData() const; /** * Returns the list of loaded payload parts. This is not necessarily * identical to all parts in the cache or to all available parts on the backend. */ QSet loadedPayloadParts() const; /** * Sets the @p revision number of the item. * * @note Do not modify this value from within an application, * it is updated automatically by the revision checking functions. */ void setRevision( int revision ); /** * Returns the revision number of the item. */ int revision() const; /** * Returns the unique identifier of the collection this item is stored in. There is only * a single such collection, although the item can be linked into arbitrary many * virtual collections. * Calling this method makes sense only after running an ItemFetchJob on the item. * @returns the collection ID if it is know, -1 otherwise. * @since 4.3 */ Entity::Id storageCollectionId() const; /** * Set the size of the item in bytes. * * @since 4.2 */ void setSize( qint64 size ); /** * Returns the size of the items in bytes. * * @since 4.2 */ qint64 size() const; /** * Sets the mime type of the item to @p mimeType. */ void setMimeType( const QString &mimeType ); /** * Returns the mime type of the item. */ QString mimeType() const; /** * Sets the payload object of this PIM item. * * @param p The payload object. Must be copyable and must not be a pointer, * will cause a compilation failure otherwise. Using a type that can be copied * fast (such as implicitly shared classes) is recommended. * If the payload type is polymorphic and you intend to set and retrieve payload * objects with mismatching but castable types, make sure to use a supported * shared pointer implementation (currently boost::shared_ptr and QSharedPointer) * and make sure there is a specialization of Akonadi::super_trait for your class. */ template void setPayload( const T &p ); //@cond PRIVATE template void setPayload( T* p ); template void setPayload( std::auto_ptr p ); //@endcond /** * Returns the payload object of this PIM item. This method will only succeed if either * you requested the exact same payload type that was put in or the payload uses a * supported shared pointer type (currently boost::shared_ptr and QSharedPointer), and * is castable to the requested type. For this to work there needs to be a specialization * of Akonadi::super_trait of the used classes. * * If a mismatching or non-castable payload type is requested, an Akonadi::PayloadException * is thrown. Therefore it is generally recommended to guard calls to payload() with a * corresponding hasPayload() call. * * Trying to retrieve a pointer type will fail to compile. */ template T payload() const; /** * Returns whether the item has a payload object. */ bool hasPayload() const; /** * Returns whether the item has a payload of type @c T. * This method will only return @c true if either you requested the exact same payload type * that was put in or the payload uses a supported shared pointer type (currently boost::shared_ptr * and QSharedPointer), and is castable to the requested type. For this to work there needs * to be a specialization of Akonadi::super_trait of the used classes. * * Trying to retrieve a pointer type will fail to compile. */ template bool hasPayload() const; /** * Describes the type of url which is returned in url(). */ enum UrlType { UrlShort = 0, ///< A short url which contains the identifier only (default) UrlWithMimeType = 1 ///< A url with identifier and mimetype }; /** * Returns the url of the item. */ KUrl url( UrlType type = UrlShort ) const; + /** + * Returns the parts available for this item. + * + * @since 4.4 + */ + QSet availablePayloadParts() const; + + /** + * Merges the Item @p other into this item. + * Any parts or attributes available in other, will be merged into this item, + * and the payload parts of other will be inserted into this item, overwriting + * any existing parts with the same part name. + * + * If there is an ItemSerialzerPluginV2 for the type, the merge method in that plugin is + * used to perform the merge. If only an ItemSerialzerPlugin class is found, or the merge + * method of the -V2 plugin is not implemented, the merge is performed with multiple deserializations + * of the payload. + * + * @since 4.4 + */ + void merge(const Item &other); + private: //@cond PRIVATE friend class ItemModifyJob; friend class ItemFetchJob; PayloadBase* payloadBase() const; void setPayloadBase( PayloadBase* ); /** * Set the collection ID to where the item is stored in. Should be set only by the ItemFetchJob. * @param collectionId the unique identifier of the the collection where this item is stored in. * @since 4.3 */ void setStorageCollectionId( Entity::Id collectionId); //@endcond AKONADI_DECLARE_PRIVATE( Item ) }; template T Item::payload() const { BOOST_STATIC_ASSERT( !boost::is_pointer::value ); if ( !payloadBase() ) throw PayloadException( "No payload set" ); typedef Internal::PayloadTrait PayloadType; if ( PayloadType::isPolymorphic ) { try { const typename PayloadType::SuperType sp = payload(); return PayloadType::castFrom( sp ); } catch ( const Akonadi::PayloadException& ) {} } Payload *p = Internal::payload_cast( payloadBase() ); if ( !p ) { throw PayloadException( QString::fromLatin1( "Wrong payload type (is '%1', requested '%2')" ) .arg( QLatin1String( payloadBase()->typeName() ) ) .arg( QLatin1String( typeid(p).name() ) ) ); } return p->payload; } template bool Item::hasPayload() const { BOOST_STATIC_ASSERT( !boost::is_pointer::value ); if ( !hasPayload() ) return false; typedef Internal::PayloadTrait PayloadType; if ( PayloadType::isPolymorphic ) { try { const typename PayloadType::SuperType sp = payload(); return PayloadType::canCastFrom( sp ); } catch ( const Akonadi::PayloadException& ) {} } return Internal::payload_cast( payloadBase() ); } template void Item::setPayload( const T &p ) { typedef Internal::PayloadTrait PayloadType; if ( PayloadType::isPolymorphic ) { const typename PayloadType::SuperType sp = PayloadType::template castTo( p ); if ( !Internal::PayloadTrait::isNull( sp ) || PayloadType::isNull( p ) ) { setPayload( sp ); return; } } setPayloadBase( new Payload( p ) ); } template void Item::setPayload( T* p ) { p.You_MUST_NOT_use_a_pointer_as_payload; } template void Item::setPayload( std::auto_ptr p ) { p.Nice_try_but_a_std_auto_ptr_is_not_allowed_as_payload_either; } } Q_DECLARE_METATYPE(Akonadi::Item) Q_DECLARE_METATYPE(Akonadi::Item::List) #endif diff --git a/akonadi/itemserializer.cpp b/akonadi/itemserializer.cpp index 024cc85a6..7dff7d235 100644 --- a/akonadi/itemserializer.cpp +++ b/akonadi/itemserializer.cpp @@ -1,294 +1,339 @@ /* Copyright (c) 2007 Till Adam Copyright (c) 2007 Volker Krause 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 "itemserializer_p.h" #include "item.h" #include "itemserializerplugin.h" #include "attributefactory.h" // KDE core #include #include #include // Qt #include #include #include #include #include #include #include #include // temporary #include "pluginloader_p.h" namespace Akonadi { class DefaultItemSerializerPlugin; class DefaultItemSerializerPlugin : public ItemSerializerPlugin { public: DefaultItemSerializerPlugin() { } bool deserialize( Item& item, const QByteArray& label, QIODevice& data, int ) { if ( label != Item::FullPayload ) return false; item.setPayload( data.readAll() ); return true; } void serialize( const Item& item, const QByteArray& label, QIODevice& data, int& ) { Q_ASSERT( label == Item::FullPayload ); if ( item.hasPayload() ) data.write( item.payload() ); } }; K_GLOBAL_STATIC( DefaultItemSerializerPlugin, s_defaultItemSerializerPlugin ) class PluginEntry { public: PluginEntry() : mPlugin( 0 ) { } explicit PluginEntry( const QString &identifier, ItemSerializerPlugin *plugin = 0 ) : mIdentifier( identifier ), mPlugin( plugin ) { } inline ItemSerializerPlugin* plugin() const { if ( mPlugin ) return mPlugin; QObject *object = PluginLoader::self()->createForName( mIdentifier ); if ( !object ) { kWarning() << "ItemSerializerPluginLoader: " << "plugin" << mIdentifier << "is not valid!" << endl; // we try to use the default in that case mPlugin = s_defaultItemSerializerPlugin; } mPlugin = qobject_cast( object ); if ( !mPlugin ) { kWarning() << "ItemSerializerPluginLoader: " << "plugin" << mIdentifier << "doesn't provide interface ItemSerializerPlugin!" << endl; // we try to use the default in that case mPlugin = s_defaultItemSerializerPlugin; } Q_ASSERT( mPlugin ); return mPlugin; } QString type() const { return mIdentifier; } bool operator<( const PluginEntry &other ) const { return mIdentifier < other.mIdentifier; } bool operator<( const QString &type ) const { return mIdentifier < type; } private: QString mIdentifier; mutable ItemSerializerPlugin *mPlugin; }; static bool operator<( const QString &type, const PluginEntry &entry ) { return type < entry.type(); } class PluginRegistry { public: PluginRegistry() : mDefaultPlugin( PluginEntry( QLatin1String("application/octet-stream"), s_defaultItemSerializerPlugin ) ) { const PluginLoader* pl = PluginLoader::self(); if ( !pl ) { kWarning() << "Cannot instantiate plugin loader!" << endl; return; } const QStringList types = pl->types(); kDebug() << "ItemSerializerPluginLoader: " << "found" << types.size() << "plugins." << endl; allPlugins.reserve( types.size() + 1 ); foreach ( const QString &type, types ) allPlugins.append( PluginEntry( type ) ); allPlugins.append( mDefaultPlugin ); std::sort( allPlugins.begin(), allPlugins.end() ); } const PluginEntry& findBestMatch( const QString &type ) { KMimeType::Ptr mimeType = KMimeType::mimeType( type, KMimeType::ResolveAliases ); if ( mimeType.isNull() ) return mDefaultPlugin; // step 1: find all plugins that match at all QVector matchingIndexes; for ( int i = 0, end = allPlugins.size(); i < end; ++i ) { if ( mimeType->is( allPlugins[i].type() ) ) matchingIndexes.append( i ); } // 0 matches: no luck (shouldn't happend though, as application/octet-stream matches everything) if ( matchingIndexes.isEmpty() ) return mDefaultPlugin; // 1 match: we are done if ( matchingIndexes.size() == 1 ) return allPlugins[matchingIndexes.first()]; // step 2: if we have more than one match, find the most specific one using topological sort boost::adjacency_list<> graph( matchingIndexes.size() ); for ( int i = 0, end = matchingIndexes.size() ; i != end ; ++i ) { KMimeType::Ptr mimeType = KMimeType::mimeType( allPlugins[matchingIndexes[i]].type(), KMimeType::ResolveAliases ); if ( mimeType.isNull() ) continue; for ( int j = 0; j != end; ++j ) { if ( i != j && mimeType->is( allPlugins[matchingIndexes[j]].type() ) ) boost::add_edge( j, i, graph ); } } QVector order; order.reserve( allPlugins.size() ); try { boost::topological_sort( graph, std::back_inserter( order ) ); } catch ( boost::not_a_dag &e ) { kWarning() << "Mimetype tree is not a DAG!"; return mDefaultPlugin; } return allPlugins[matchingIndexes[order.first()]]; } QVector allPlugins; QHash cachedPlugins; private: PluginEntry mDefaultPlugin; }; K_GLOBAL_STATIC( PluginRegistry, s_pluginRegistry ) /*static*/ void ItemSerializer::deserialize( Item& item, const QByteArray& label, const QByteArray& data, int version, bool external ) { if ( external ) { QFile file( QString::fromUtf8(data) ); if ( file.open( QIODevice:: ReadOnly ) ) { deserialize( item, label, file, version ); file.close(); } } else { QBuffer buffer; buffer.setData( data ); buffer.open( QIODevice::ReadOnly ); buffer.seek( 0 ); deserialize( item, label, buffer, version ); buffer.close(); } } /*static*/ void ItemSerializer::deserialize( Item& item, const QByteArray& label, QIODevice& data, int version ) { - if ( !ItemSerializer::pluginForMimeType( item.mimeType() ).deserialize( item, label, data, version ) ) + if ( !ItemSerializer::pluginForMimeType( item.mimeType() )->deserialize( item, label, data, version ) ) kWarning() << "Unable to deserialize payload part:" << label; } /*static*/ void ItemSerializer::serialize( const Item& item, const QByteArray& label, QByteArray& data, int &version ) { QBuffer buffer; buffer.setBuffer( &data ); buffer.open( QIODevice::WriteOnly ); buffer.seek( 0 ); serialize( item, label, buffer, version ); buffer.close(); } /*static*/ void ItemSerializer::serialize( const Item& item, const QByteArray& label, QIODevice& data, int &version ) { if ( !item.hasPayload() ) return; - ItemSerializerPlugin& plugin = pluginForMimeType( item.mimeType() ); - plugin.serialize( item, label, data, version ); + ItemSerializerPlugin *plugin = pluginForMimeType( item.mimeType() ); + plugin->serialize( item, label, data, version ); } -QSet ItemSerializer::parts(const Item & item) +void ItemSerializer::merge( Item &item, const Item &other ) +{ + if ( !other.hasPayload() ) + return; + ItemSerializerPlugin *plugin = pluginForMimeType( item.mimeType() ); + + ItemSerializerPluginV2 *pluginV2 = dynamic_cast(plugin); + if ( pluginV2 ) + { + pluginV2->merge( item, other ); + return; + } + + // Old-school merge: + QBuffer buffer; + buffer.setBuffer( &other.payloadData() ); + buffer.open( QIODevice::ReadOnly ); + + foreach( const QByteArray &part, other.loadedPayloadParts() ) + { + buffer.seek( 0 ); + deserialize( item, part, buffer, 0 ); + } + buffer.close(); + +} + +QSet ItemSerializer::parts( const Item & item ) +{ + if ( !item.hasPayload() ) + return QSet(); + return pluginForMimeType( item.mimeType() )->parts( item ); +} + +QSet ItemSerializer::availableParts( const Item & item ) { if ( !item.hasPayload() ) return QSet(); - return pluginForMimeType( item.mimeType() ).parts( item ); + ItemSerializerPlugin *plugin = pluginForMimeType( item.mimeType() ); + ItemSerializerPluginV2 *pluginV2 = dynamic_cast( plugin ); + + if ( pluginV2 ) + { + return pluginV2->availableParts( item ); + } + + if (item.hasPayload()) + return QSet(); + return QSet() << Item::FullPayload; + } /*static*/ -ItemSerializerPlugin& ItemSerializer::pluginForMimeType( const QString & mimetype ) +ItemSerializerPlugin* ItemSerializer::pluginForMimeType( const QString & mimetype ) { // plugin cached, so let's take that one if ( s_pluginRegistry->cachedPlugins.contains( mimetype ) ) - return *(s_pluginRegistry->cachedPlugins.value( mimetype )); + return s_pluginRegistry->cachedPlugins.value( mimetype ); ItemSerializerPlugin *plugin = 0; // check if we have one that matches exactly const QVector::const_iterator it = qBinaryFind( s_pluginRegistry->allPlugins.constBegin(), s_pluginRegistry->allPlugins.constEnd(), mimetype ); if ( it != s_pluginRegistry->allPlugins.constEnd() ) { - plugin = (*it).plugin(); + plugin = ( *it ).plugin(); } else { // check if we have a more generic plugin const PluginEntry &entry = s_pluginRegistry->findBestMatch( mimetype ); kDebug() << "Did not find exactly matching serializer plugin for type" << mimetype << ", taking" << entry.type() << "as the closest match"; plugin = entry.plugin(); } Q_ASSERT(plugin); s_pluginRegistry->cachedPlugins.insert( mimetype, plugin ); - return *plugin; + return plugin; } } diff --git a/akonadi/itemserializer_p.h b/akonadi/itemserializer_p.h index fa996e672..321ef6fa9 100644 --- a/akonadi/itemserializer_p.h +++ b/akonadi/itemserializer_p.h @@ -1,61 +1,73 @@ /* Copyright (c) 2007 Till Adam Copyright (c) 2007 Volker Krause 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. */ #ifndef AKONADI_ITEM_SERIALIZER_P_H #define AKONADI_ITEM_SERIALIZER_P_H #include #include #include "akonadiprivate_export.h" class QIODevice; namespace Akonadi { class Item; class ItemSerializerPlugin; /** @internal Serialization/Deserialization of item parts, serializer plugin management. */ class AKONADI_TESTS_EXPORT ItemSerializer { public: /** throws ItemSerializerException on failure */ static void deserialize( Item& item, const QByteArray& label, const QByteArray& data, int version, bool external ); /** throws ItemSerializerException on failure */ static void deserialize( Item& item, const QByteArray& label, QIODevice& data, int version ); /** throws ItemSerializerException on failure */ static void serialize( const Item& item, const QByteArray& label, QByteArray& data, int &version ); /** throws ItemSerializerException on failure */ static void serialize( const Item& item, const QByteArray& label, QIODevice& data, int &version ); + /** throws ItemSerializerException on failure + * + * @since 4.4 + */ + static void merge( Item& item, const Item &other ); + /** Returns a list of parts available in the item payload. */ static QSet parts( const Item &item ); + /** Returns a list of parts available remotely in the item payload. + * + * @since 4.4 + */ + static QSet availableParts( const Item &item ); + private: - static ItemSerializerPlugin& pluginForMimeType( const QString& mimetype ); + static ItemSerializerPlugin* pluginForMimeType( const QString& mimetype ); }; } #endif diff --git a/akonadi/itemserializerplugin.cpp b/akonadi/itemserializerplugin.cpp index 0631d7c10..7c3e0c1c4 100644 --- a/akonadi/itemserializerplugin.cpp +++ b/akonadi/itemserializerplugin.cpp @@ -1,36 +1,63 @@ /* Copyright (c) 2007 Till Adam Copyright (c) 2007 Volker Krause 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 "itemserializerplugin.h" #include "item.h" +#include + using namespace Akonadi; ItemSerializerPlugin::~ItemSerializerPlugin() { } QSet ItemSerializerPlugin::parts(const Item & item) const { QSet set; if ( item.hasPayload() ) set.insert( Item::FullPayload ); return set; } + +ItemSerializerPluginV2::~ItemSerializerPluginV2() +{ +} + +QSet ItemSerializerPluginV2::availableParts(const Item & item) const +{ + if ( item.hasPayload() ) + return QSet(); + return QSet() << Item::FullPayload; +} + +void ItemSerializerPluginV2::merge(Item &item, const Item &other ) +{ + QBuffer buffer; + buffer.setBuffer( &other.payloadData() ); + buffer.open( QIODevice::ReadOnly ); + + foreach( const QByteArray part, other.loadedPayloadParts() ) + { + buffer.seek( 0 ); + deserialize( item, part, buffer, 0 ); + } + buffer.close(); +} diff --git a/akonadi/itemserializerplugin.h b/akonadi/itemserializerplugin.h index aae3801b0..021537a86 100644 --- a/akonadi/itemserializerplugin.h +++ b/akonadi/itemserializerplugin.h @@ -1,181 +1,215 @@ /* Copyright (c) 2007 Till Adam Copyright (c) 2007 Volker Krause 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. */ #ifndef AKONADI_ITEMSERIALIZERPLUGIN_H #define AKONADI_ITEMSERIALIZERPLUGIN_H #include #include +#include "item.h" #include "akonadi_export.h" class QIODevice; namespace Akonadi { -class Item; /** * @short The base class for item type serializer plugins. * * Serializer plugins convert between the payload of Akonadi::Item objects and * a textual or binary representation of the actual content data. * This allows to easily add support for new types to Akonadi. * * The following example shows how to implement a serializer plugin for * a new data type PimNote. * * The PimNote data structure: * @code * typedef struct { * QString author; * QDateTime dateTime; * QString text; * } PimNote; * @endcode * * The serializer plugin code: * @code * #include * * class SerializerPluginPimNote : public QObject, public Akonadi::ItemSerializerPlugin * { * Q_OBJECT * Q_INTERFACES( Akonadi::ItemSerializerPlugin ) * * public: * bool deserialize( Akonadi::Item& item, const QByteArray& label, QIODevice& data, int version ) * { * // we don't handle versions in this example * Q_UNUSED( version ); * * // we work only on full payload * if ( label != Akonadi::Item::FullPayload ) * return false; * * QDataStream stream( &data ); * * PimNote note; * stream >> note.author; * stream >> note.dateTime; * stream >> note.text; * * item.setPayload( note ); * * return true; * } * * void serialize( const Akonadi::Item& item, const QByteArray& label, QIODevice& data, int &version ) * { * // we don't handle versions in this example * Q_UNUSED( version ); * * if ( label != Akonadi::Item::FullPayload || !item.hasPayload() ) * return; * * QDataStream stream( &data ); * * PimNote note = item.payload(); * * stream << note.author; * stream << note.dateTime; * stream << note.text; * } * }; * * Q_EXPORT_PLUGIN2( akonadi_serializer_pimnote, SerializerPluginPimNote ) * * @endcode * * The desktop file: * @code * [Misc] * Name=Pim Note Serializer * Comment=An Akonadi serializer plugin for note objects * * [Plugin] * Type=application/x-pimnote * X-KDE-Library=akonadi_serializer_pimnote * @endcode * * @author Till Adam , Volker Krause */ class AKONADI_EXPORT ItemSerializerPlugin { public: /** * Destroys the item serializer plugin. */ virtual ~ItemSerializerPlugin(); /** * Converts serialized item data provided in @p data into payload for @p item. * * @param item The item to which the payload should be added. * It is guaranteed to have a mime type matching one of the supported * mime types of this plugin. * However it might contain a unsuited payload added manually * by the application developer. * Verifying the payload type in case a payload is already available * is recommended therefore. * @param label The part identifier of the part to deserialize. * @p label might be an unsupported item part, return @c false if this is the case. * @param data A QIODevice providing access to the serialized data. * The QIODevice is opened in read-only mode and positioned at the beginning. * The QIODevice is guaranteed to be valid. * @param version The version of the data format as set by the user in serialize() or @c 0 (default). * @return @c false if the specified part is not supported by this plugin, @c true if the part * could be de-serialized successfully. */ virtual bool deserialize( Item& item, const QByteArray& label, QIODevice& data, int version ) = 0; /** * Convert the payload object provided in @p item into its serialzed form into @p data. * * @param item The item which contains the payload. * It is guaranteed to have a mimetype matching one of the supported * mimetypes of this plugin as well as the existence of a payload object. * However it might contain an unsupported payload added manually by * the application developer. * Verifying the payload type is recommended therefore. * @param label The part identifier of the part to serialize. * @p label will be one of the item parts returned by parts(). * @param data The QIODevice where the serialized data should be written to. * The QIODevice is opened in write-only mode and positioned at the beginning. * The QIODevice is guaranteed to be valid. * @param version The version of the data format. Can be set by the user to handle different * versions. */ virtual void serialize( const Item& item, const QByteArray& label, QIODevice& data, int &version ) = 0; /** * Returns a list of available parts for the given item payload. * The default implementation returns Item::FullPayload if a payload is set. * * @param item The item. */ virtual QSet parts( const Item &item ) const; + +}; + +class AKONADI_EXPORT ItemSerializerPluginV2 : public ItemSerializerPlugin +{ +public: + /** + * Destroys the item serializer plugin. + */ + virtual ~ItemSerializerPluginV2(); + + /** + * Merges the payload parts in @p other into @p item. + * + * The default implementation is slow as it requires serializing @p other, and deserializing @p item multiple times. + * Reimplementing this is recommended if your type uses payload parts. + * + * @since 4.4 + */ + virtual void merge( Item &item, const Item &other ); + + + /** + * Returns the parts available in the item @p item. + * + * This should be reimplemented to return available parts. + * + * The default implementation returns an empty set if the item has a payload, + * and a set containing Item::FullPayload if the item has no payload. + * + * @since 4.4 + */ + virtual QSet availableParts( const Item &item ) const; }; } Q_DECLARE_INTERFACE( Akonadi::ItemSerializerPlugin, "org.freedesktop.Akonadi.ItemSerializerPlugin/1.0" ) +Q_DECLARE_INTERFACE( Akonadi::ItemSerializerPluginV2, "org.freedesktop.Akonadi.ItemSerializerPlugin/1.1" ) #endif