File indexing completed on 2024-06-23 05:06:49

0001 /*
0002     SPDX-FileCopyrightText: 2006 Volker Krause <vkrause@kde.org>
0003                   2007 Till Adam <adam@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #pragma once
0009 
0010 #include "akonadicore_export.h"
0011 #include "attribute.h"
0012 #include "collection.h"
0013 #include "exceptionbase.h"
0014 #include "itempayloadinternals_p.h"
0015 #include "job.h"
0016 #include "relation.h"
0017 #include "tag.h"
0018 
0019 #include <QByteArray>
0020 #include <QMetaType>
0021 #include <QSet>
0022 
0023 #include <memory>
0024 #include <type_traits>
0025 #include <typeinfo>
0026 
0027 class QUrl;
0028 template<typename T>
0029 class QList;
0030 
0031 namespace Akonadi
0032 {
0033 class ItemPrivate;
0034 
0035 /**
0036  * @short Represents a PIM item stored in Akonadi storage.
0037  *
0038  * A PIM item consists of one or more parts, allowing a fine-grained access on its
0039  * content where needed (eg. mail envelope, mail body and attachments).
0040  *
0041  * There is also a namespace (prefix) for special parts which are local to Akonadi.
0042  * These parts, prefixed by "akonadi-", will never be fetched in the resource.
0043  * They are useful for local extensions like agents which might want to add meta data
0044  * to items in order to handle them but the meta data should not be stored back to the
0045  * resource.
0046  *
0047  * This class is implicitly shared.
0048  *
0049  * <h4>Payload</h4>
0050  *
0051  * This class contains, beside some type-agnostic information (flags, revision),
0052  * zero or more payload objects representing its actual data. Which objects these actually
0053  * are depends on the mimetype of the item and the corresponding serializer plugin(s).
0054  *
0055  * Technically the only restriction on payload objects is that they have to be copyable.
0056  * For safety reasons, pointer payloads are forbidden as well though, as the
0057  * ownership would not be clear. In this case, usage of a shared pointer is
0058  * recommended (such as QSharedPointer or std::shared_ptr).
0059  *
0060  * Using a shared pointer is also required in case the payload is a polymorphic
0061  * type. For supported shared pointer types implicit casting is provided when possible.
0062  *
0063  * When using a value-based class as payload, it is recommended to use one that does
0064  * support implicit sharing as setting and retrieving a payload as well as copying
0065  * an Akonadi::Item object imply copying of the payload object.
0066  *
0067  * Since KDE 4.6, Item supports multiple payload types per mime type,
0068  * and will automatically convert between them using the serialiser
0069  * plugins (which is slow). It also supports mixing shared pointer
0070  * types, e.g. inserting a std::shared_ptr<Foo> and extracting a
0071  * QSharedPointer<Foo>. Since the two shared pointer types cannot
0072  * share ownership of the same object, the payload class @c T needs to
0073  * provide a @c clone() method with the usual signature, ie.
0074  *
0075  * @code
0076  * virtual T * T::clone() const
0077  * @endcode
0078  *
0079  * If the class that does not have a @c clone() method, asking for an
0080  * incompatible shared pointer will throw a PayloadException.
0081  *
0082  * Since using different shared pointer types and different payload
0083  * types for the same mimetype incurs slow conversions (between
0084  * payload types) and cloning (between shared pointer types), as well
0085  * as manifold memory usage (results of conversions are cached inside
0086  * the Item, and only destroyed when a new payload is set by the user
0087  * of the class), you want to restrict yourself to just one type and
0088  * one shared pointer type. This mechanism was mainly introduced for
0089  * backwards compatibility (e.g., putting in a
0090  * std::shared_ptr<KCal::Incidence> and extracting a
0091  * QSharedPointer<KCalCore::Incidence>), so it is not optimized for
0092  * performance.
0093  *
0094  * The availability of a payload of a specific type can be checked using hasPayload(),
0095  * payloads can be retrieved by using payload() and set by using setPayload(). Refer
0096  * to the documentation of those methods for more details.
0097  *
0098  * @author Volker Krause <vkrause@kde.org>, Till Adam <adam@kde.org>, Marc Mutz <mutz@kde.org>
0099  */
0100 class AKONADICORE_EXPORT Item
0101 {
0102 public:
0103     /**
0104      * Describes the unique id type.
0105      */
0106     using Id = qint64;
0107 
0108     /**
0109      * Describes a list of items.
0110      */
0111     using List = QList<Item>;
0112 
0113     /**
0114      * Describes a flag name.
0115      */
0116     using Flag = QByteArray;
0117 
0118     /**
0119      * Describes a set of flag names.
0120      */
0121     using Flags = QSet<QByteArray>;
0122 
0123     /**
0124      * Describes the part name that is used to fetch the
0125      * full payload of an item.
0126      */
0127     static const char FullPayload[];
0128 
0129     /**
0130      * Creates a new item.
0131      */
0132     Item();
0133 
0134     /**
0135      * Creates a new item with the given unique @p id.
0136      */
0137     explicit Item(Id id);
0138 
0139     /**
0140      * Creates a new item with the given mime type.
0141      *
0142      * @param mimeType The mime type of the item.
0143      */
0144     explicit Item(const QString &mimeType);
0145 
0146     /**
0147      * Creates a new item from an @p other item.
0148      */
0149     Item(const Item &other);
0150 
0151     /**
0152      * Move constructor.
0153      */
0154     Item(Item &&) noexcept;
0155 
0156     /**
0157      * Destroys the item.
0158      */
0159     ~Item();
0160 
0161     /**
0162      * Creates an item from the given @p url.
0163      */
0164     static Item fromUrl(const QUrl &url);
0165 
0166     /**
0167      * Sets the unique @p identifier of the item.
0168      */
0169     void setId(Id identifier);
0170 
0171     /**
0172      * Returns the unique identifier of the item.
0173      */
0174     Id id() const;
0175 
0176     /**
0177      * Sets the remote @p id of the item.
0178      */
0179     void setRemoteId(const QString &id);
0180 
0181     /**
0182      * Returns the remote id of the item.
0183      */
0184     QString remoteId() const;
0185 
0186     /**
0187      * Sets the remote @p revision of the item.
0188      * @param revision the item's remote revision
0189      * The remote revision can be used by resources to store some
0190      * revision information of the backend to detect changes there.
0191      *
0192      * @note This method is supposed to be used by resources only.
0193      * @since 4.5
0194      */
0195     void setRemoteRevision(const QString &revision);
0196 
0197     /**
0198      * Returns the remote revision of the item.
0199      *
0200      * @note This method is supposed to be used by resources only.
0201      * @since 4.5
0202      */
0203     QString remoteRevision() const;
0204 
0205     /**
0206      * Returns whether the item is valid.
0207      */
0208     bool isValid() const;
0209 
0210     /**
0211      * Returns whether this item's id equals the id of the @p other item.
0212      */
0213     bool operator==(const Item &other) const;
0214 
0215     /**
0216      * Returns whether the item's id does not equal the id of the @p other item.
0217      */
0218     bool operator!=(const Item &other) const;
0219 
0220     /**
0221      * Assigns the @p other to this item and returns a reference to this item.
0222      * @param other the item to assign
0223      */
0224     Item &operator=(const Item &other);
0225 
0226     /**
0227      * @internal For use with containers only.
0228      *
0229      * @since 4.8
0230      */
0231     bool operator<(const Item &other) const;
0232 
0233     /**
0234      * Returns the parent collection of this object.
0235      * @note This will of course only return a useful value if it was explicitly retrieved
0236      *       from the Akonadi server.
0237      * @since 4.4
0238      */
0239     Collection parentCollection() const;
0240 
0241     /**
0242      * Returns a reference to the parent collection of this object.
0243      * @note This will of course only return a useful value if it was explicitly retrieved
0244      *       from the Akonadi server.
0245      * @since 4.4
0246      */
0247     Collection &parentCollection();
0248 
0249     /**
0250      * Set the parent collection of this object.
0251      * @note Calling this method has no immediate effect for the object itself,
0252      *       such as being moved to another collection.
0253      *       It is mainly relevant to provide a context for RID-based operations
0254      *       inside resources.
0255      * @param parent The parent collection.
0256      * @since 4.4
0257      */
0258     void setParentCollection(const Collection &parent);
0259 
0260     /**
0261      * Adds an attribute to the item.
0262      *
0263      * If an attribute of the same type name already exists, it is deleted and
0264      * replaced with the new one.
0265      *
0266      * @param attribute The new attribute.
0267      *
0268      * @note The collection takes the ownership of the attribute.
0269      */
0270     void addAttribute(Attribute *attribute);
0271 
0272     /**
0273      * Removes and deletes the attribute of the given type @p name.
0274      */
0275     void removeAttribute(const QByteArray &name);
0276 
0277     /**
0278      * Returns @c true if the item has an attribute of the given type @p name,
0279      * false otherwise.
0280      */
0281     bool hasAttribute(const QByteArray &name) const;
0282 
0283     /**
0284      * Returns a list of all attributes of the item.
0285      *
0286      * @warning Do not modify the attributes returned from this method,
0287      * the change will not be reflected when updating the Item through
0288      * ItemModifyJob.
0289      */
0290     Attribute::List attributes() const;
0291 
0292     /**
0293      * Removes and deletes all attributes of the item.
0294      */
0295     void clearAttributes();
0296 
0297     /**
0298      * Returns the attribute of the given type @p name if available, 0 otherwise.
0299      */
0300     Attribute *attribute(const QByteArray &name);
0301     const Attribute *attribute(const QByteArray &name) const;
0302 
0303     /**
0304      * Describes the options that can be passed to access attributes.
0305      */
0306     enum CreateOption {
0307         AddIfMissing, ///< Creates the attribute if it is missing
0308         DontCreate ///< Do not create the attribute if it is missing (default)
0309     };
0310 
0311     /**
0312      * Returns the attribute of the requested type.
0313      * If the item has no attribute of that type yet, a new one
0314      * is created and added to the entity.
0315      *
0316      * @param option The create options.
0317      */
0318     template<typename T>
0319     inline T *attribute(CreateOption option = DontCreate);
0320 
0321     /**
0322      * Returns the attribute of the requested type or 0 if it is not available.
0323      */
0324     template<typename T>
0325     inline const T *attribute() const;
0326 
0327     /**
0328      * Removes and deletes the attribute of the requested type.
0329      */
0330     template<typename T>
0331     inline void removeAttribute();
0332 
0333     /**
0334      * Returns whether the item has an attribute of the requested type.
0335      */
0336     template<typename T>
0337     inline bool hasAttribute() const;
0338 
0339     /**
0340      * Returns all flags of this item.
0341      */
0342     Flags flags() const;
0343 
0344     /**
0345      * Returns the timestamp of the last modification of this item.
0346      * @since 4.2
0347      */
0348     QDateTime modificationTime() const;
0349 
0350     /**
0351      * Sets the timestamp of the last modification of this item.
0352      * @param datetime the modification time to set
0353      * @note Do not modify this value from within an application,
0354      * it is updated automatically by the revision checking functions.
0355      * @since 4.2
0356      */
0357     void setModificationTime(const QDateTime &datetime);
0358 
0359     /**
0360      * Returns whether the flag with the given @p name is
0361      * set in the item.
0362      */
0363     bool hasFlag(const QByteArray &name) const;
0364 
0365     /**
0366      * Sets the flag with the given @p name in the item.
0367      */
0368     void setFlag(const QByteArray &name);
0369 
0370     /**
0371      * Removes the flag with the given @p name from the item.
0372      */
0373     void clearFlag(const QByteArray &name);
0374 
0375     /**
0376      * Overwrites all flags of the item by the given @p flags.
0377      */
0378     void setFlags(const Flags &flags);
0379 
0380     /**
0381      * Removes all flags from the item.
0382      */
0383     void clearFlags();
0384 
0385     void setTags(const Tag::List &list);
0386 
0387     void setTag(const Tag &tag);
0388 
0389     Tag::List tags() const;
0390 
0391     bool hasTag(const Tag &tag) const;
0392 
0393     void clearTag(const Tag &tag);
0394 
0395     void clearTags();
0396 
0397     /**
0398      * Returns all relations of this item.
0399      * @since 4.15
0400      * @see RelationCreateJob, RelationDeleteJob to modify relations
0401      */
0402     Relation::List relations() const;
0403 
0404     /**
0405      * Sets the payload based on the canonical representation normally
0406      * used for data of this mime type.
0407      *
0408      * @param data The encoded data.
0409      * @see fullPayloadData
0410      */
0411     void setPayloadFromData(const QByteArray &data);
0412 
0413     /**
0414      * Returns the full payload in its canonical representation, e.g. the
0415      * binary or textual format usually used for data with this mime type.
0416      * This is useful when communicating with non-Akonadi application by
0417      * e.g. drag&drop, copy&paste or stored files.
0418      */
0419     QByteArray payloadData() const;
0420 
0421     /**
0422      * Returns the list of loaded payload parts. This is not necessarily
0423      * identical to all parts in the cache or to all available parts on the backend.
0424      */
0425     QSet<QByteArray> loadedPayloadParts() const;
0426 
0427     /**
0428      * Marks that the payload shall be cleared from the cache when this
0429      * item is passed to an ItemModifyJob the next time.
0430      * This will trigger a refetch of the payload from the backend when the
0431      * item is accessed afterwards. Only resources should have a need for
0432      * this functionality.
0433      *
0434      * @since 4.5
0435      */
0436     void clearPayload();
0437 
0438     /**
0439      * Sets the @p revision number of the item.
0440      * @param revision the revision number to set
0441      * @note Do not modify this value from within an application,
0442      * it is updated automatically by the revision checking functions.
0443      */
0444     void setRevision(int revision);
0445 
0446     /**
0447      * Returns the revision number of the item.
0448      */
0449     int revision() const;
0450 
0451     /**
0452      * Returns the unique identifier of the collection this item is stored in. There is only
0453      * a single such collection, although the item can be linked into arbitrary many
0454      * virtual collections.
0455      * Calling this method makes sense only after running an ItemFetchJob on the item.
0456      * @returns the collection ID if it is known, -1 otherwise.
0457      * @since 4.3
0458      */
0459     Collection::Id storageCollectionId() const;
0460 
0461     /**
0462      * Set the size of the item in bytes.
0463      * @param size the size of the item in bytes
0464      * @since 4.2
0465      */
0466     void setSize(qint64 size);
0467 
0468     /**
0469      * Returns the size of the items in bytes.
0470      *
0471      * @since 4.2
0472      */
0473     qint64 size() const;
0474 
0475     /**
0476      * Sets the mime type of the item to @p mimeType.
0477      */
0478     void setMimeType(const QString &mimeType);
0479 
0480     /**
0481      * Returns the mime type of the item.
0482      */
0483     QString mimeType() const;
0484 
0485     /**
0486      * Sets the @p gid of the entity.
0487      *
0488      * @since 4.12
0489      */
0490     void setGid(const QString &gid);
0491 
0492     /**
0493      * Returns the gid of the entity.
0494      *
0495      * @since 4.12
0496      */
0497     QString gid() const;
0498 
0499     /**
0500      * Sets the virtual @p collections that this item is linked into.
0501      *
0502      * @note Note that changing this value makes no effect on what collections
0503      * this item is linked to. To link or unlink an item to/from a virtual
0504      * collection, use LinkJob and UnlinkJob.
0505      *
0506      * @since 4.14
0507      */
0508     void setVirtualReferences(const Collection::List &collections);
0509 
0510     /**
0511      * Lists virtual collections that this item is linked to.
0512      *
0513      * @note This value is populated only when this item was retrieved by
0514      * ItemFetchJob with fetchVirtualReferences set to true in ItemFetchScope,
0515      * otherwise this list is always empty.
0516      *
0517      * @since 4.14
0518      */
0519     Collection::List virtualReferences() const;
0520 
0521     /**
0522      * Returns a list of metatype-ids, describing the different
0523      * variants of payload that are currently contained in this item.
0524      *
0525      * The result is always sorted (increasing ids).
0526      */
0527     QList<int> availablePayloadMetaTypeIds() const;
0528 
0529     /**
0530      * Sets a path to a file with full payload.
0531      *
0532      * This method can only be used by Resources and should not be used by Akonadi
0533      * clients. Clients should use setPayload() instead.
0534      *
0535      * Akonadi will not duplicate content of the file in its database but will
0536      * instead directly refer to this file. This means that the file must be
0537      * persistent (don't use this method with a temporary files), and the Akonadi
0538      * resource that owns the storage is responsible for updating the file path
0539      * if the file is changed, moved or removed.
0540      *
0541      * The payload can still be accessed via payload() methods.
0542      *
0543      * @see setPayload(), setPayloadFromData()
0544      * @since 5.6
0545      */
0546     void setPayloadPath(const QString &filePath);
0547 
0548     /**
0549      * Returns path to the payload file set by setPayloadPath()
0550      *
0551      * If payload was set via setPayload() or setPayloadFromData() then this
0552      * method will return a null string.
0553      */
0554     QString payloadPath() const;
0555 
0556     /**
0557      * Sets the payload object of this PIM item.
0558      *
0559      * @param p The payload object. Must be copyable and must not be a pointer,
0560      * will cause a compilation failure otherwise. Using a type that can be copied
0561      * fast (such as implicitly shared classes) is recommended.
0562      * If the payload type is polymorphic and you intend to set and retrieve payload
0563      * objects with mismatching but castable types, make sure to use a supported
0564      * shared pointer implementation (currently QSharedPointer and std::shared_ptr)
0565      * and make sure there is a specialization of Akonadi::super_trait for your class.
0566      */
0567     template<typename T>
0568     void setPayload(const T &p);
0569     /// @cond PRIVATE
0570     template<typename T>
0571     void setPayload(T *p);
0572     /// @endcond
0573 
0574     /**
0575      * Returns the payload object of this PIM item. This method will only succeed if either
0576      * you requested the exact same payload type that was put in or the payload uses a
0577      * supported shared pointer type (currently QSharedPointer and std::shared_ptr),
0578      * and is castable to the requested type. For this to work there needs
0579      * to be a specialization of Akonadi::super_trait of the used classes.
0580      *
0581      * If a mismatching or non-castable payload type is requested, an Akonadi::PayloadException
0582      * is thrown. Therefore it is generally recommended to guard calls to payload() with a
0583      * corresponding hasPayload() call.
0584      *
0585      * Trying to retrieve a pointer type will fail to compile.
0586      */
0587     template<typename T>
0588     T payload() const;
0589 
0590     /**
0591      * Returns whether the item has a payload object.
0592      */
0593     bool hasPayload() const;
0594 
0595     /**
0596      * Returns whether the item has a payload of type @c T.
0597      * This method will only return @c true if either you requested the exact same payload type
0598      * that was put in or the payload uses a supported shared pointer type (currently
0599      * QSharedPointer and std::shared_ptr), and is castable to the requested type. For this to work there needs
0600      * to be a specialization of Akonadi::super_trait of the used classes.
0601      *
0602      * Trying to retrieve a pointer type will fail to compile.
0603      */
0604     template<typename T>
0605     bool hasPayload() const;
0606 
0607     /**
0608      * Describes the type of url which is returned in url().
0609      */
0610     enum UrlType {
0611         UrlShort = 0, ///< A short url which contains the identifier only (default)
0612         UrlWithMimeType = 1 ///< A url with identifier and mimetype
0613     };
0614 
0615     /**
0616      * Returns the url of the item.
0617      */
0618     QUrl url(UrlType type = UrlShort) const;
0619 
0620     /**
0621      * Returns the parts available for this item.
0622      *
0623      * The returned set refers to parts available on the akonadi server or remotely,
0624      * but does not include the loadedPayloadParts() of this item.
0625      *
0626      * @since 4.4
0627      */
0628     QSet<QByteArray> availablePayloadParts() const;
0629 
0630     /**
0631      * Returns the parts available for this item in the cache. The list might be a subset
0632      * of the actual parts in cache, as it contains only the requested parts. See @see ItemFetchJob and
0633      * @see ItemFetchScope
0634      *
0635      * The returned set refers to parts available on the akonadi server.
0636      *
0637      * @since 4.11
0638      */
0639     QSet<QByteArray> cachedPayloadParts() const;
0640 
0641     /**
0642      * Applies the parts of Item @p other to this item.
0643      * Any parts or attributes available in other, will be applied to this item,
0644      * and the payload parts of other will be inserted into this item, overwriting
0645      * any existing parts with the same part name.
0646      *
0647      * If there is an ItemSerialzerPluginV2 for the type, the merge method in that plugin is
0648      * used to perform the merge. If only an ItemSerialzerPlugin class is found, or the merge
0649      * method of the -V2 plugin is not implemented, the merge is performed with multiple deserializations
0650      * of the payload.
0651      * @param other the item to get values from
0652      * @since 4.4
0653      */
0654     void apply(const Item &other);
0655 
0656     void setCachedPayloadParts(const QSet<QByteArray> &cachedParts);
0657 
0658 private:
0659     /// @cond PRIVATE
0660     friend class ItemCreateJob;
0661     friend class ItemCreateJobPrivate;
0662     friend class ItemModifyJob;
0663     friend class ItemModifyJobPrivate;
0664     friend class ItemSync;
0665     friend class ProtocolHelper;
0666     Internal::PayloadBase *payloadBaseV2(int sharedPointerId, int metaTypeId) const;
0667     void setPayloadBaseV2(int sharedPointerId, int metaTypeId, std::unique_ptr<Internal::PayloadBase> &p);
0668     void addPayloadBaseVariant(int sharedPointerId, int metaTypeId, std::unique_ptr<Internal::PayloadBase> &p) const;
0669 
0670     /**
0671      * Try to ensure that we have a variant of the payload for metatype id @a mtid.
0672      * @return @c true if a type exists or could be created through conversion, @c false otherwise.
0673      */
0674     bool ensureMetaTypeId(int mtid) const;
0675 
0676     template<typename T>
0677     typename std::enable_if<Internal::PayloadTrait<T>::isPolymorphic, void>::type setPayloadImpl(const T &p, const int * /*disambiguate*/ = nullptr);
0678     template<typename T>
0679     typename std::enable_if<!Internal::PayloadTrait<T>::isPolymorphic, void>::type setPayloadImpl(const T &p);
0680 
0681     template<typename T>
0682     typename std::enable_if<Internal::PayloadTrait<T>::isPolymorphic, T>::type payloadImpl(const int * /*disambiguate*/ = nullptr) const;
0683     template<typename T>
0684     typename std::enable_if<!Internal::PayloadTrait<T>::isPolymorphic, T>::type payloadImpl() const;
0685 
0686     template<typename T>
0687     typename std::enable_if<Internal::PayloadTrait<T>::isPolymorphic, bool>::type hasPayloadImpl(const int * /*disambiguate*/ = nullptr) const;
0688     template<typename T>
0689     typename std::enable_if<!Internal::PayloadTrait<T>::isPolymorphic, bool>::type hasPayloadImpl() const;
0690 
0691     template<typename T>
0692     typename std::enable_if<Internal::is_shared_pointer<T>::value, bool>::type tryToClone(T *ret, const int * /*disambiguate*/ = nullptr) const;
0693     template<typename T>
0694     typename std::enable_if<!Internal::is_shared_pointer<T>::value, bool>::type tryToClone(T *ret) const;
0695 
0696     template<typename T, typename NewT>
0697     typename std::enable_if<!std::is_same<T, NewT>::value, bool>::type tryToCloneImpl(T *ret, const int * /*disambiguate*/ = nullptr) const;
0698     template<typename T, typename NewT>
0699     typename std::enable_if<std::is_same<T, NewT>::value, bool>::type tryToCloneImpl(T *ret) const;
0700 
0701     /**
0702      * Set the collection ID to where the item is stored in. Should be set only by the ItemFetchJob.
0703      * @param collectionId the unique identifier of the collection where this item is stored in.
0704      * @since 4.3
0705      */
0706     void setStorageCollectionId(Collection::Id collectionId);
0707 
0708     void throwPayloadException(int spid, int mtid) const;
0709 
0710     QSharedDataPointer<ItemPrivate> d_ptr;
0711     friend class ItemPrivate;
0712     /// @endcond
0713 };
0714 
0715 AKONADICORE_EXPORT size_t qHash(const Akonadi::Item &item, size_t seed = 0) noexcept;
0716 
0717 template<typename T>
0718 inline T *Item::attribute(Item::CreateOption option)
0719 {
0720     const QByteArray type = T().type();
0721     if (hasAttribute(type)) {
0722         if (T *attr = dynamic_cast<T *>(attribute(type))) {
0723             return attr;
0724         }
0725         qWarning() << "Found attribute of unknown type" << type << ". Did you forget to call AttributeFactory::registerAttribute()?";
0726     } else if (option == AddIfMissing) {
0727         T *attr = new T();
0728         addAttribute(attr);
0729         return attr;
0730     }
0731 
0732     return nullptr;
0733 }
0734 
0735 template<typename T>
0736 inline const T *Item::attribute() const
0737 {
0738     const QByteArray type = T().type();
0739     if (hasAttribute(type)) {
0740         if (const T *attr = dynamic_cast<const T *>(attribute(type))) {
0741             return attr;
0742         }
0743         qWarning() << "Found attribute of unknown type" << type << ". Did you forget to call AttributeFactory::registerAttribute()?";
0744     }
0745 
0746     return nullptr;
0747 }
0748 
0749 template<typename T>
0750 inline void Item::removeAttribute()
0751 {
0752     removeAttribute(T().type());
0753 }
0754 
0755 template<typename T>
0756 inline bool Item::hasAttribute() const
0757 {
0758     return hasAttribute(T().type());
0759 }
0760 
0761 template<typename T>
0762 T Item::payload() const
0763 {
0764     static_assert(!std::is_pointer<T>::value, "Payload must not be a pointer");
0765 
0766     if (!hasPayload()) {
0767         throwPayloadException(-1, -1);
0768     }
0769 
0770     return payloadImpl<T>();
0771 }
0772 
0773 template<typename T>
0774 typename std::enable_if<Internal::PayloadTrait<T>::isPolymorphic, T>::type Item::payloadImpl(const int *) const
0775 {
0776     using PayloadType = Internal::PayloadTrait<T>;
0777     static_assert(PayloadType::isPolymorphic, "Non-polymorphic payload type in polymorphic implementation is not allowed");
0778 
0779     using Root_T = typename Internal::get_hierarchy_root<T>::type;
0780     using RootType = Internal::PayloadTrait<Root_T>;
0781     static_assert(!RootType::isPolymorphic,
0782                   "Root type of payload type must not be polymorphic"); // prevent endless recursion
0783 
0784     return PayloadType::castFrom(payloadImpl<Root_T>());
0785 }
0786 
0787 template<typename T>
0788 typename std::enable_if<!Internal::PayloadTrait<T>::isPolymorphic, T>::type Item::payloadImpl() const
0789 {
0790     using PayloadType = Internal::PayloadTrait<T>;
0791     static_assert(!PayloadType::isPolymorphic, "Polymorphic payload type in non-polymorphic implementation is not allowed");
0792 
0793     const int metaTypeId = PayloadType::elementMetaTypeId();
0794 
0795     // make sure that we have a payload format represented by 'metaTypeId':
0796     if (!ensureMetaTypeId(metaTypeId)) {
0797         throwPayloadException(PayloadType::sharedPointerId, metaTypeId);
0798     }
0799 
0800     // Check whether we have the exact payload
0801     // (metatype id and shared pointer type match)
0802     if (const Internal::Payload<T> *const p = Internal::payload_cast<T>(payloadBaseV2(PayloadType::sharedPointerId, metaTypeId))) {
0803         return p->payload;
0804     }
0805 
0806     T ret;
0807     if (!tryToClone<T>(&ret)) {
0808         throwPayloadException(PayloadType::sharedPointerId, metaTypeId);
0809     }
0810     return ret;
0811 }
0812 
0813 template<typename T, typename NewT>
0814 typename std::enable_if<!std::is_same<T, NewT>::value, bool>::type Item::tryToCloneImpl(T *ret, const int *) const
0815 {
0816     using PayloadType = Internal::PayloadTrait<T>;
0817     using NewPayloadType = Internal::PayloadTrait<NewT>;
0818 
0819     const int metaTypeId = PayloadType::elementMetaTypeId();
0820     Internal::PayloadBase *payloadBase = payloadBaseV2(NewPayloadType::sharedPointerId, metaTypeId);
0821     if (const Internal::Payload<NewT> *const p = Internal::payload_cast<NewT>(payloadBase)) {
0822         // If found, attempt to make a clone (required the payload to provide virtual T * T::clone() const)
0823         const T nt = PayloadType::clone(p->payload);
0824         if (!PayloadType::isNull(nt)) {
0825             // if clone succeeded, add the clone to the Item:
0826             std::unique_ptr<Internal::PayloadBase> npb(new Internal::Payload<T>(nt));
0827             addPayloadBaseVariant(PayloadType::sharedPointerId, metaTypeId, npb);
0828             // and return it
0829             if (ret) {
0830                 *ret = nt;
0831             }
0832             return true;
0833         }
0834     }
0835 
0836     return tryToCloneImpl<T, typename Internal::shared_pointer_traits<NewT>::next_shared_ptr>(ret);
0837 }
0838 
0839 template<typename T, typename NewT>
0840 typename std::enable_if<std::is_same<T, NewT>::value, bool>::type Item::tryToCloneImpl(T *) const
0841 {
0842     return false;
0843 }
0844 
0845 template<typename T>
0846 typename std::enable_if<Internal::is_shared_pointer<T>::value, bool>::type Item::tryToClone(T *ret, const int *) const
0847 {
0848     using PayloadType = Internal::PayloadTrait<T>;
0849     static_assert(!PayloadType::isPolymorphic, "Polymorphic payload type in non-polymorphic implementation is not allowed");
0850 
0851     return tryToCloneImpl<T, typename Internal::shared_pointer_traits<T>::next_shared_ptr>(ret);
0852 }
0853 
0854 template<typename T>
0855 typename std::enable_if<!Internal::is_shared_pointer<T>::value, bool>::type Item::tryToClone(T *) const
0856 {
0857     using PayloadType = Internal::PayloadTrait<T>;
0858     static_assert(!PayloadType::isPolymorphic, "Polymorphic payload type in non-polymorphic implementation is not allowed");
0859 
0860     return false;
0861 }
0862 
0863 template<typename T>
0864 bool Item::hasPayload() const
0865 {
0866     static_assert(!std::is_pointer<T>::value, "Payload type cannot be a pointer");
0867     return hasPayload() && hasPayloadImpl<T>();
0868 }
0869 
0870 template<typename T>
0871 typename std::enable_if<Internal::PayloadTrait<T>::isPolymorphic, bool>::type Item::hasPayloadImpl(const int *) const
0872 {
0873     using PayloadType = Internal::PayloadTrait<T>;
0874     static_assert(PayloadType::isPolymorphic, "Non-polymorphic payload type in polymorphic implementation is no allowed");
0875 
0876     using Root_T = typename Internal::get_hierarchy_root<T>::type;
0877     using RootType = Internal::PayloadTrait<Root_T>;
0878     static_assert(!RootType::isPolymorphic,
0879                   "Root type of payload type must not be polymorphic"); // prevent endless recursion
0880 
0881     try {
0882         return hasPayloadImpl<Root_T>() && PayloadType::canCastFrom(payload<Root_T>());
0883     } catch (const Akonadi::PayloadException &e) {
0884         qDebug() << e.what();
0885         Q_UNUSED(e)
0886         return false;
0887     }
0888 }
0889 
0890 template<typename T>
0891 typename std::enable_if<!Internal::PayloadTrait<T>::isPolymorphic, bool>::type Item::hasPayloadImpl() const
0892 {
0893     using PayloadType = Internal::PayloadTrait<T>;
0894     static_assert(!PayloadType::isPolymorphic, "Polymorphic payload type in non-polymorphic implementation is not allowed");
0895 
0896     const int metaTypeId = PayloadType::elementMetaTypeId();
0897 
0898     // make sure that we have a payload format represented by 'metaTypeId':
0899     if (!ensureMetaTypeId(metaTypeId)) {
0900         return false;
0901     }
0902 
0903     // Check whether we have the exact payload
0904     // (metatype id and shared pointer type match)
0905     if (const Internal::Payload<T> *const p = Internal::payload_cast<T>(payloadBaseV2(PayloadType::sharedPointerId, metaTypeId))) {
0906         return true;
0907     }
0908 
0909     return tryToClone<T>(nullptr);
0910 }
0911 
0912 template<typename T>
0913 void Item::setPayload(const T &p)
0914 {
0915     static_assert(!std::is_pointer<T>::value, "Payload type must not be a pointer");
0916     setPayloadImpl(p);
0917 }
0918 
0919 template<typename T>
0920 typename std::enable_if<Internal::PayloadTrait<T>::isPolymorphic>::type Item::setPayloadImpl(const T &p, const int *)
0921 {
0922     using PayloadType = Internal::PayloadTrait<T>;
0923     static_assert(PayloadType::isPolymorphic, "Non-polymorphic payload type in polymorphic implementation is not allowed");
0924 
0925     using Root_T = typename Internal::get_hierarchy_root<T>::type;
0926     using RootType = Internal::PayloadTrait<Root_T>;
0927     static_assert(!RootType::isPolymorphic,
0928                   "Root type of payload type must not be polymorphic"); // prevent endless recursion
0929 
0930     setPayloadImpl<Root_T>(p);
0931 }
0932 
0933 template<typename T>
0934 typename std::enable_if<!Internal::PayloadTrait<T>::isPolymorphic>::type Item::setPayloadImpl(const T &p)
0935 {
0936     using PayloadType = Internal::PayloadTrait<T>;
0937     std::unique_ptr<Internal::PayloadBase> pb(new Internal::Payload<T>(p));
0938     setPayloadBaseV2(PayloadType::sharedPointerId, PayloadType::elementMetaTypeId(), pb);
0939 }
0940 
0941 template<typename T>
0942 void Item::setPayload(T *p)
0943 {
0944     p->You_MUST_NOT_use_a_pointer_as_payload;
0945 }
0946 
0947 } // namespace Akonadi
0948 
0949 Q_DECLARE_METATYPE(Akonadi::Item)
0950 Q_DECLARE_METATYPE(Akonadi::Item::List)
0951 Q_DECLARE_TYPEINFO(Akonadi::Item, Q_RELOCATABLE_TYPE);