/* 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 #include #include class KUrl; template class QVector; 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 is implicitly shared. * *

Payload

* * This class contains, beside some type-agnostic information (flags, revision), * zero or more payload objects representing its actual data. Which objects these actually * are depends on the mimetype of the item and the corresponding serializer plugin(s). * * 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 required in case the payload is a polymorphic * type. 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. * * Since KDE 4.6, Item supports multiple payload types per mime type, * and will automatically convert between them using the serialiser * plugins (which is slow). It also supports mixing shared pointer * types, e.g. inserting a boost::shared_ptr and extracting a * QSharedPointer. Since the two shared pointer types cannot * share ownership of the same object, the payload class @c T needs to * provide a @c clone() method with the usual signature, ie. * * @code * virtual T * T::clone() const * @endcode * * If the class that does not have a @c clone() method, asking for an * incompatible shared pointer will throw a PayloadException. * * Since using different shared pointer types and different payload * types for the same mimetype incurs slow conversions (between * payload types) and cloning (between shared pointer types), as well * as manifold memory usage (results of conversions are cached inside * the Item, and only destroyed when a new payload is set by the user * of the class), you want to restrict yourself to just one type and * one shared pointer type. This mechanism was mainly introduced for * backwards compatibility (e.g., putting in a * boost::shared_ptr and extracting a * QSharedPointer), so it is not optimized for * performance. * * 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 , Marc Mutz */ 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; /** * Marks that the payload shall be cleared from the cache when this * item is passed to an ItemModifyJob the next time. * This will trigger a refetch of the payload from the backend when the * item is accessed afterwards. Only resources should have a need for * this functionality. * * @since 4.5 */ void clearPayload(); /** * 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 known, -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; /** * Returns a list of metatype-ids, describing the different * variants of payload that are currently contained in this item. * * The result is always sorted (increasing ids). */ QVector availablePayloadMetaTypeIds() 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. * * The returned set refers to parts available on the akonadi server or remotely, * but does not include the loadedPayloadParts() of this item. * * @since 4.4 */ QSet availablePayloadParts() const; /** * Applies the parts of Item @p other to this item. * Any parts or attributes available in other, will be applied to 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 apply( const Item &other ); /** * Registers \a T as a legacy type for mime type \a mimeType. * * This is required information for Item to return the correct * type from payload() when clients have not been recompiled to * use the new code. * * @since 4.6 */ template static void addToLegacyMapping( const QString & mimeType ); private: //@cond PRIVATE friend class ItemCreateJob; friend class ItemModifyJob; friend class ItemSync; friend class ProtocolHelper; PayloadBase* payloadBase() const; void setPayloadBase( PayloadBase* ); PayloadBase* payloadBaseV2( int sharedPointerId, int metaTypeId ) const; //std::auto_ptr takePayloadBase( int sharedPointerId, int metaTypeId ); void setPayloadBaseV2( int sharedPointerId, int metaTypeId, std::auto_ptr p ); void addPayloadBaseVariant( int sharedPointerId, int metaTypeId, std::auto_ptr p ) const; static void addToLegacyMappingImpl( const QString & mimeType, int sharedPointerId, int metaTypeId, std::auto_ptr p ); /** * Try to ensure that we have a variant of the payload for metatype id @a mtid. * @return @c true if a type exists or could be created through conversion, @c false otherwise. */ bool ensureMetaTypeId( int mtid ) const; template typename boost::enable_if_c::isPolymorphic,void>::type setPayloadImpl( const T &, const int * /*disambiguate*/ = 0 ); template typename boost::disable_if_c::isPolymorphic,void>::type setPayloadImpl( const T & ); template typename boost::enable_if_c::isPolymorphic,T>::type payloadImpl( const int * /*disambiguate*/ = 0 ) const; template typename boost::disable_if_c::isPolymorphic,T>::type payloadImpl() const; template typename boost::enable_if_c::isPolymorphic,bool>::type hasPayloadImpl( const int * /*disambiguate*/ = 0 ) const; template typename boost::disable_if_c::isPolymorphic,bool>::type hasPayloadImpl() const; template typename boost::enable_if,bool>::type tryToClone( T *, const int * /*disambiguate*/ = 0 ) const; template typename boost::disable_if,bool>::type tryToClone( T * ) const; /** * 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); #if 0 /** * Helper function for non-template throwing of PayloadException. */ QString payloadExceptionText( int spid, int mtid ) const; /** * Non-template throwing of PayloadException. * Needs to be inline, otherwise catch (Akonadi::PayloadException) * won't work (only catch (Akonadi::Exception)) */ inline void throwPayloadException( int spid, int mtid ) const { throw PayloadException( payloadExceptionText( spid, mtid ) ); } #else void throwPayloadException( int spid, int mtid ) const; #endif //@endcond AKONADI_DECLARE_PRIVATE( Item ) }; template T Item::payload() const { BOOST_STATIC_ASSERT( !boost::is_pointer::value ); if ( !hasPayload() ) throwPayloadException( -1, -1 ); return payloadImpl(); } template typename boost::enable_if_c::isPolymorphic,T>::type Item::payloadImpl( const int * ) const { typedef Internal::PayloadTrait PayloadType; BOOST_STATIC_ASSERT(( PayloadType::isPolymorphic )); typedef typename Internal::get_hierarchy_root::type Root_T; typedef Internal::PayloadTrait RootType; BOOST_STATIC_ASSERT(( !RootType::isPolymorphic )); // prevent endless recursion return PayloadType::castFrom( payloadImpl() ); } template typename boost::disable_if_c::isPolymorphic,T>::type Item::payloadImpl() const { typedef Internal::PayloadTrait PayloadType; BOOST_STATIC_ASSERT(( !PayloadType::isPolymorphic )); const int metaTypeId = PayloadType::elementMetaTypeId(); // make sure that we have a payload format represented by 'metaTypeId': if ( !ensureMetaTypeId( metaTypeId ) ) throwPayloadException( PayloadType::sharedPointerId, metaTypeId ); // Check whether we have the exact payload // (metatype id and shared pointer type match) if ( const Payload * const p = Internal::payload_cast( payloadBaseV2( PayloadType::sharedPointerId, metaTypeId ) ) ) return p->payload; T ret; if ( !tryToClone( &ret ) ) throwPayloadException( PayloadType::sharedPointerId, metaTypeId ); return ret; } template typename boost::enable_if,bool>::type Item::tryToClone( T * ret, const int * ) const { typedef Internal::PayloadTrait PayloadType; BOOST_STATIC_ASSERT(( !PayloadType::isPolymorphic )); const int metaTypeId = PayloadType::elementMetaTypeId(); // Check whether we have the same payload in 'the other // shared pointer' (### make it recurse, trying to find one, but // don't introduce infinite recursion): typedef typename Internal::shared_pointer_traits::next_shared_ptr NewT; typedef Internal::PayloadTrait NewPayloadType; if ( const Payload * const p = Internal::payload_cast( payloadBaseV2( NewPayloadType::sharedPointerId, metaTypeId ) ) ) { // If found, attempt to make a clone (required the payload to provide virtual T * T::clone() const) const T nt = PayloadType::clone( p->payload ); if ( !PayloadType::isNull( nt ) ) { // if clone succeeded, add the clone to the Item: std::auto_ptr npb( new Payload( nt ) ); addPayloadBaseVariant( PayloadType::sharedPointerId, metaTypeId, npb ); // and return it if ( ret ) *ret = nt; return true; } } return false; } template typename boost::disable_if, bool>::type Item::tryToClone( T * ) const { typedef Internal::PayloadTrait PayloadType; BOOST_STATIC_ASSERT(( !PayloadType::isPolymorphic )); return false; } template bool Item::hasPayload() const { BOOST_STATIC_ASSERT( !boost::is_pointer::value ); return hasPayload() && hasPayloadImpl(); } template typename boost::enable_if_c::isPolymorphic,bool>::type Item::hasPayloadImpl( const int * ) const { typedef Internal::PayloadTrait PayloadType; BOOST_STATIC_ASSERT(( PayloadType::isPolymorphic )); typedef typename Internal::get_hierarchy_root::type Root_T; typedef Internal::PayloadTrait RootType; BOOST_STATIC_ASSERT(( !RootType::isPolymorphic )); // prevent endless recursion try { return hasPayloadImpl() && PayloadType::canCastFrom( payload() ); } catch ( const Akonadi::PayloadException & ) { return false; } } template typename boost::disable_if_c::isPolymorphic,bool>::type Item::hasPayloadImpl() const { typedef Internal::PayloadTrait PayloadType; BOOST_STATIC_ASSERT(( !PayloadType::isPolymorphic )); const int metaTypeId = PayloadType::elementMetaTypeId(); // make sure that we have a payload format represented by 'metaTypeId': if ( !ensureMetaTypeId( metaTypeId ) ) return false; // Check whether we have the exact payload // (metatype id and shared pointer type match) if ( const Payload * const p = Internal::payload_cast( payloadBaseV2( PayloadType::sharedPointerId, metaTypeId ) ) ) return true; return tryToClone( 0 ); } template void Item::setPayload( const T &p ) { BOOST_STATIC_ASSERT(( !boost::is_pointer::value )); setPayloadImpl( p ); } template typename boost::enable_if_c::isPolymorphic>::type Item::setPayloadImpl( const T & p, const int * ) { typedef Internal::PayloadTrait PayloadType; BOOST_STATIC_ASSERT(( PayloadType::isPolymorphic )); typedef typename Internal::get_hierarchy_root::type Root_T; typedef Internal::PayloadTrait RootType; BOOST_STATIC_ASSERT(( !RootType::isPolymorphic )); // prevent endless recursion setPayloadImpl( p ); } template typename boost::disable_if_c::isPolymorphic>::type Item::setPayloadImpl( const T & p ) { typedef Internal::PayloadTrait PayloadType; std::auto_ptr pb( new Payload( p ) ); setPayloadBaseV2( PayloadType::sharedPointerId, PayloadType::elementMetaTypeId(), pb ); } 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; } template void Item::addToLegacyMapping( const QString & mimeType ) { typedef Internal::PayloadTrait PayloadType; BOOST_STATIC_ASSERT(( !PayloadType::isPolymorphic )); std::auto_ptr p( new Payload ); addToLegacyMappingImpl( mimeType, PayloadType::sharedPointerId, PayloadType::elementMetaTypeId(), p ); } } Q_DECLARE_METATYPE(Akonadi::Item) Q_DECLARE_METATYPE(Akonadi::Item::List) #endif