diff --git a/src/agentbase/agentfactory.h b/src/agentbase/agentfactory.h index 751f637dd..b5cecaabe 100644 --- a/src/agentbase/agentfactory.h +++ b/src/agentbase/agentfactory.h @@ -1,107 +1,107 @@ /* This file is part of akonadiresources. Copyright (c) 2006 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_AGENTFACTORY_H #define AKONADI_AGENTFACTORY_H #include "akonadiagentbase_export.h" #include "agentbase.h" #include namespace Akonadi { class AgentFactoryBasePrivate; /** * @short A factory base class for in-process agents. * * @see AKONADI_AGENT_FACTORY() * @internal * @since 4.6 */ class AKONADIAGENTBASE_EXPORT AgentFactoryBase : public QObject { Q_OBJECT public: /** * Creates a new agent factory. * Executed in the main thread, performs KDE infrastructure setup. * * @param catalogName The translation catalog of this resource. * @param parent The parent object. */ explicit AgentFactoryBase(const char *catalogName, QObject *parent = nullptr); virtual ~AgentFactoryBase(); public Q_SLOTS: /** * Creates a new agent instace with the given identifier. */ virtual QObject *createInstance(const QString &identifier) const = 0; protected: void createComponentData(const QString &identifier) const; private: AgentFactoryBasePrivate *const d; }; /** * @short A factory for in-process agents. * * @see AKONADI_AGENT_FACTORY() * @internal * @since 4.6 */ template class AgentFactory : public AgentFactoryBase { public: /** reimplemented */ explicit AgentFactory(const char *catalogName, QObject *parent = nullptr) : AgentFactoryBase(catalogName, parent) { } QObject *createInstance(const QString &identifier) const Q_DECL_OVERRIDE { createComponentData(identifier); T *instance = new T(identifier); // check if T also inherits AgentBase::Observer and // if it does, automatically register it on itself Akonadi::AgentBase::Observer *observer = dynamic_cast(instance); - if (observer != 0) { + if (observer != nullptr) { instance->registerObserver(observer); } return instance; } }; } #endif diff --git a/src/agentbase/resourcebase.h b/src/agentbase/resourcebase.h index 79d10916d..789edb107 100644 --- a/src/agentbase/resourcebase.h +++ b/src/agentbase/resourcebase.h @@ -1,898 +1,898 @@ /* This file is part of akonadiresources. Copyright (c) 2006 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_RESOURCEBASE_H #define AKONADI_RESOURCEBASE_H #include "akonadiagentbase_export.h" #include "agentbase.h" #include "collection.h" #include "item.h" #include "itemsync.h" class KJob; class Akonadi__ResourceAdaptor; class ResourceState; namespace Akonadi { class ResourceBasePrivate; /** * @short The base class for all Akonadi resources. * * This class should be used as a base class by all resource agents, * because it encapsulates large parts of the protocol between * resource agent, agent manager and the Akonadi storage. * * It provides many convenience methods to make implementing a * new Akonadi resource agent as simple as possible. * *

How to write a resource

* * The following provides an overview of what you need to do to implement * your own Akonadi resource. In the following, the term 'backend' refers * to the entity the resource connects with Akonadi, be it a single file * or a remote server. * * @todo Complete this (online/offline state management) * *
Basic %Resource Framework
* * The following is needed to create a new resource: * - A new class deriving from Akonadi::ResourceBase, implementing at least all * pure-virtual methods, see below for further details. * - call init() in your main() function. * - a .desktop file similar to the following example * \code * [Desktop Entry] * Encoding=UTF-8 * Name=My Akonadi Resource * Type=AkonadiResource * Exec=akonadi_my_resource * Icon=my-icon * * X-Akonadi-MimeTypes= * X-Akonadi-Capabilities=Resource * X-Akonadi-Identifier=akonadi_my_resource * \endcode * *
Handling PIM Items
* * To follow item changes in the backend, the following steps are necessary: * - Implement retrieveItems() to synchronize all items in the given * collection. If the backend supports incremental retrieval, * implementing support for that is recommended to improve performance. * - Convert the items provided by the backend to Akonadi items. * This typically happens either in retrieveItems() if you retrieved * the collection synchronously (not recommended for network backends) or * in the result slot of the asynchronous retrieval job. * Converting means to create Akonadi::Item objects for every retrieved * item. It's very important that every object has its remote identifier set. * - Call itemsRetrieved() or itemsRetrievedIncremental() respectively * with the item objects created above. The Akonadi storage will then be * updated automatically. Note that it is usually not necessary to manipulate * any item in the Akonadi storage manually. * * To fetch item data on demand, the method retrieveItem() needs to be * reimplemented. Fetch the requested data there and call itemRetrieved() * with the result item. * * To write local changes back to the backend, you need to re-implement * the following three methods: * - itemAdded() * - itemChanged() * - itemRemoved() * * Note that these three functions don't get the full payload of the items by default, * you need to change the item fetch scope of the change recorder to fetch the full * payload. This can be expensive with big payloads, though.
* Once you have handled changes in these methods, call changeCommitted(). * These methods are called whenever a local item related to this resource is * added, modified or deleted. They are only called if the resource is online, otherwise * all changes are recorded and replayed as soon the resource is online again. * *
Handling Collections
* * To follow collection changes in the backend, the following steps are necessary: * - Implement retrieveCollections() to retrieve collections from the backend. * If the backend supports incremental collections updates, implementing * support for that is recommended to improve performance. * - Convert the collections of the backend to Akonadi collections. * This typically happens either in retrieveCollections() if you retrieved * the collection synchronously (not recommended for network backends) or * in the result slot of the asynchronous retrieval job. * Converting means to create Akonadi::Collection objects for every retrieved * collection. It's very important that every object has its remote identifier * and its parent remote identifier set. * - Call collectionsRetrieved() or collectionsRetrievedIncremental() respectively * with the collection objects created above. The Akonadi storage will then be * updated automatically. Note that it is usually not necessary to manipulate * any collection in the Akonadi storage manually. * * * To write local collection changes back to the backend, you need to re-implement * the following three methods: * - collectionAdded() * - collectionChanged() * - collectionRemoved() * Once you have handled changes in these methods call changeCommitted(). * These methods are called whenever a local collection related to this resource is * added, modified or deleted. They are only called if the resource is online, otherwise * all changes are recorded and replayed as soon the resource is online again. * * @todo Convenience base class for collection-less resources */ // FIXME_API: API dox need to be updated for Observer approach (kevin) class AKONADIAGENTBASE_EXPORT ResourceBase : public AgentBase { Q_OBJECT public: /** * Use this method in the main function of your resource * application to initialize your resource subclass. * This method also takes care of creating a KApplication * object and parsing command line arguments. * * @note In case the given class is also derived from AgentBase::Observer * it gets registered as its own observer (see AgentBase::Observer), e.g. * resourceInstance->registerObserver( resourceInstance ); * * @code * * class MyResource : public ResourceBase * { * ... * }; * * int main( int argc, char **argv ) * { * return ResourceBase::init( argc, argv ); * } * * @endcode * * @param argc number of arguments * @param argv string arguments */ template static int init(int argc, char **argv) { // Disable session management qunsetenv("SESSION_MANAGER"); QApplication app(argc, argv); const QString id = parseArguments(argc, argv); T *r = new T(id); // check if T also inherits AgentBase::Observer and // if it does, automatically register it on itself Observer *observer = dynamic_cast(r); - if (observer != 0) { + if (observer != nullptr) { r->registerObserver(observer); } return init(r); } /** * This method is used to set the name of the resource. */ void setName(const QString &name); /** * Returns the name of the resource. */ QString name() const; /** * Enable or disable automatic progress reporting. By default, it is enabled. * When enabled, the resource will automatically emit the signals percent() and status() * while syncing items or collections. * * The automatic progress reporting is done on a per item / per collection basis, so if a * finer granularity is desired, automatic reporting should be disabled and the subclass should * emit the percent() and status() signals itself. * * @param enabled Whether or not automatic emission of the signals is enabled. * @since 4.7 */ void setAutomaticProgressReporting(bool enabled); Q_SIGNALS: /** * This signal is emitted whenever the name of the resource has changed. * * @param name The new name of the resource. */ void nameChanged(const QString &name); /** * Emitted when a full synchronization has been completed. */ void synchronized(); /** * Emitted when a collection attributes synchronization has been completed. * * @param collectionId The identifier of the collection whose attributes got synchronized. * @since 4.6 */ void attributesSynchronized(qlonglong collectionId); /** * Emitted when a collection tree synchronization has been completed. * * @since 4.8 */ void collectionTreeSynchronized(); /** * Emitted when the item synchronization processed the current batch and is ready for a new one. * Use this to throttle the delivery to not overload Akonadi. * * Throttling can be used during item retrieval (retrieveItems(Akonadi::Collection)) in streaming mode. * To throttle only deliver itemSyncBatchSize() items, and wait for this signal, then again deliver * @param remainingBatchSize items. * * By always only providing the number of items required to process the batch, the items don't pile * up in memory and items are only provided as fast as Akonadi can process them. * * @see batchSize() * * @since 4.14 */ void retrieveNextItemSyncBatch(int remainingBatchSize); protected Q_SLOTS: /** * Retrieve the collection tree from the remote server and supply it via * collectionsRetrieved() or collectionsRetrievedIncremental(). * @see collectionsRetrieved(), collectionsRetrievedIncremental() */ virtual void retrieveCollections() = 0; /** * Retrieve all tags from the backend * @see tagsRetrieved() */ virtual void retrieveTags(); /** * Retrieve all relations from the backend * @see relationsRetrieved() */ virtual void retrieveRelations(); /** * Retrieve the attributes of a single collection from the backend. The * collection to retrieve attributes for is provided as @p collection. * Add the attributes parts and call collectionAttributesRetrieved() * when done. * * @param collection The collection whose attributes should be retrieved. * @see collectionAttributesRetrieved() * @since 4.6 */ virtual void retrieveCollectionAttributes(const Akonadi::Collection &collection); /** * Retrieve all (new/changed) items in collection @p collection. * It is recommended to use incremental retrieval if the backend supports that * and provide the result by calling itemsRetrievedIncremental(). * If incremental retrieval is not possible, provide the full listing by calling * itemsRetrieved( const Item::List& ). * In any case, ensure that all items have a correctly set remote identifier * to allow synchronizing with items already existing locally. * In case you don't want to use the built-in item syncing code, store the retrieved * items manually and call itemsRetrieved() once you are done. * @param collection The collection whose items to retrieve. * @see itemsRetrieved( const Item::List& ), itemsRetrievedIncremental(), itemsRetrieved(), currentCollection(), batchSize() */ virtual void retrieveItems(const Akonadi::Collection &collection) = 0; /** * Returns the batch size used during the item sync. * * This can be used to throttle the item delivery. * * @see retrieveNextItemSyncBatch(int), retrieveItems(Akonadi::Collection) * @since 4.14 */ int itemSyncBatchSize() const; /** * Set the batch size used during the item sync. * The default is 10. * * @see retrieveNextItemSyncBatch(int) * @since 4.14 */ void setItemSyncBatchSize(int batchSize); /** * Set to true to scheudle an attribute sync before every item sync. * The default is false. * * @since 4.15 */ void setScheduleAttributeSyncBeforeItemSync(bool); /** * Retrieve a single item from the backend. The item to retrieve is provided as @p item. * Add the requested payload parts and call itemRetrieved() when done. * @param item The empty item whose payload should be retrieved. Use this object when delivering * the result instead of creating a new item to ensure conflict detection will work. * @param parts The item parts that should be retrieved. * @return false if there is an immediate error when retrieving the item. * @see itemRetrieved() * @deprecated Use retrieveItems(const Akonadi::Item::List &, const QSet &) instead. */ AKONADIAGENTBASE_DEPRECATED virtual bool retrieveItem(const Akonadi::Item &item, const QSet &parts); /** * Retrieve given @p items from the backend. * Add the requested payload parts and call itemsRetrieved() when done. * It is guaranteed that all @p items in the list belong to the same Collection. * * @param items The items whose payload should be retrieved. Use those objects * when delivering the result instead of creating new items to ensure conflict * detection will work. * @param parts The item parts that should be retrieved. * @return false if there is an immeidate error when retrieving the items. * @see itemsRetrieved() * @since 5.4 * * @todo: Make this method pure virtual once retrieveItem() is gone */ virtual bool retrieveItems(const Akonadi::Item::List &items, const QSet &parts); /** * Abort any activity in progress in the backend. By default this method does nothing. * * @since 4.6 */ virtual void abortActivity(); /** * Dump resource internals, for debugging. * @since 4.9 */ virtual QString dumpResourceToString() const { return QString(); } protected: /** * Creates a base resource. * * @param id The instance id of the resource. */ ResourceBase(const QString &id); /** * Destroys the base resource. */ ~ResourceBase(); /** * Call this method from retrieveItem() once the result is available. * * @param item The retrieved item. */ void itemRetrieved(const Item &item); /** * Call this method from retrieveCollectionAttributes() once the result is available. * * @param collection The collection whose attributes got retrieved. * @since 4.6 */ void collectionAttributesRetrieved(const Collection &collection); /** * Resets the dirty flag of the given item and updates the remote id. * * Call whenever you have successfully written changes back to the server. * This implicitly calls changeProcessed(). * @param item The changed item. */ void changeCommitted(const Item &item); /** * Resets the dirty flag of all given items and updates remote ids. * * Call whenever you have successfully written changes back to the server. * This implicitly calls changeProcessed(). * @param items Changed items * * @since 4.11 */ void changesCommitted(const Item::List &items); /** * Resets the dirty flag of the given tag and updates the remote id. * * Call whenever you have successfully written changes back to the server. * This implicitly calls changeProcessed(). * @param tag Changed tag. * * @since 4.13 */ void changeCommitted(const Tag &tag); /** * Call whenever you have successfully handled or ignored a collection * change notification. * * This will update the remote identifier of @p collection if necessary, * as well as any other collection attributes. * This implicitly calls changeProcessed(). * @param collection The collection which changes have been handled. */ void changeCommitted(const Collection &collection); /** * Call this to supply the full folder tree retrieved from the remote server. * * @param collections A list of collections. * @see collectionsRetrievedIncremental() */ void collectionsRetrieved(const Collection::List &collections); void tagsRetrieved(const Tag::List &tags, const QHash &tagMembers); void relationsRetrieved(const Relation::List &relations); /** * Call this to supply incrementally retrieved collections from the remote server. * * @param changedCollections Collections that have been added or changed. * @param removedCollections Collections that have been deleted. * @see collectionsRetrieved() */ void collectionsRetrievedIncremental(const Collection::List &changedCollections, const Collection::List &removedCollections); /** * Enable collection streaming, that is collections don't have to be delivered at once * as result of a retrieveCollections() call but can be delivered by multiple calls * to collectionsRetrieved() or collectionsRetrievedIncremental(). When all collections * have been retrieved, call collectionsRetrievalDone(). * @param enable @c true if collection streaming should be enabled, @c false by default */ void setCollectionStreamingEnabled(bool enable); /** * Call this method to indicate you finished synchronizing the collection tree. * * This is not needed if you use the built in syncing without collection streaming * and call collectionsRetrieved() or collectionRetrievedIncremental() instead. * If collection streaming is enabled, call this method once all collections have been delivered * using collectionsRetrieved() or collectionsRetrievedIncremental(). */ void collectionsRetrievalDone(); /** * Allows to keep locally changed collection parts during the collection sync. * * This is useful for resources to be able to provide default values during the collection * sync, while preserving eventual more up-to date values. * * Valid values are attribute types and "CONTENTMIMETYPE" for the collections content mimetypes. * * By default this is enabled for the EntityDisplayAttribute. * * @param parts A set parts for which local changes should be preserved. * @since 4.14 */ void setKeepLocalCollectionChanges(const QSet &parts); /** * Call this method to supply the full collection listing from the remote server. Items not present in the list * will be dropped from the Akonadi database. * * If the remote server supports incremental listing, it's strongly * recommended to use itemsRetrievedIncremental() instead. * @param items A list of items. * @see itemsRetrievedIncremental(). */ void itemsRetrieved(const Item::List &items); /** * Call this method when you want to use the itemsRetrieved() method * in streaming mode and indicate the amount of items that will arrive * that way. * * @warning By default this will end the item sync automatically once * sufficient items were delivered. To disable this and only make use * of the progress reporting, use setDisableAutomaticItemDeliveryDone() * * @note The recommended way is therefore: * @code * setDisableAutomaticItemDeliveryDone(true); * setItemStreamingEnabled(true); * setTotalItems(X); // X = sum of all items in all batches * while (...) { * itemsRetrievedIncremental(...); * // or itemsRetrieved(...); * } * itemsRetrievalDone(); * @endcode * * @param amount number of items that will arrive in streaming mode * @see setDisableAutomaticItemDeliveryDone(bool) * @see setItemStreamingEnabled(bool) */ void setTotalItems(int amount); /** * Disables the automatic completion of the item sync, * based on the number of delivered items. * * This ensures that the item sync only finishes once itemsRetrieved() * is called, while still making it possible to use the automatic progress * reporting based on setTotalItems(). * * @note This needs to be called once, before the item sync is started. * * @see setTotalItems(int) * @since 4.14 */ void setDisableAutomaticItemDeliveryDone(bool disable); /** * Enable item streaming, which is disabled by default. * Item streaming means that the resource can call setTotalItems(), * and then itemsRetrieved() or itemsRetrievedIncremental() multiple times, * in chunks. When all is done, the resource should call itemsRetrievalDone(). * @param enable @c true if items are delivered in chunks rather in one big block. * @see setTotalItems(int) */ void setItemStreamingEnabled(bool enable); /** * Set transaction mode for item sync'ing. * @param mode item transaction mode * @see Akonadi::ItemSync::TransactionMode * @since 4.6 */ void setItemTransactionMode(ItemSync::TransactionMode mode); /** * Set merge mode for item sync'ing. * * Default merge mode is RIDMerge. * * @note This method must be called before first call to itemRetrieved(), * itemsRetrieved() or itemsRetrievedIncremental(). * * @param mode Item merging mode (see ItemCreateJob for details on item merging) * @see Akonadi::ItemSync::MergeMode * @ince 4.14.11 */ void setItemMergingMode(ItemSync::MergeMode mode); /** * Set the fetch scope applied for item synchronization. * By default, the one set on the changeRecorder() is used. However, it can make sense * to specify a specialized fetch scope for synchronization to improve performance. * The rule of thumb is to remove anything from this fetch scope that does not provide * additional information regarding whether and item has changed or not. This is primarily * relevant for backends not supporting incremental retrieval. * @param fetchScope The fetch scope to use by the internal Akonadi::ItemSync instance. * @see Akonadi::ItemSync * @since 4.6 */ void setItemSynchronizationFetchScope(const ItemFetchScope &fetchScope); /** * Call this method to supply incrementally retrieved items from the remote server. * * @param changedItems Items changed in the backend. * @param removedItems Items removed from the backend. */ void itemsRetrievedIncremental(const Item::List &changedItems, const Item::List &removedItems); /** * Call this method to indicate you finished synchronizing the current collection. * * This is not needed if you use the built in syncing without item streaming * and call itemsRetrieved() or itemsRetrievedIncremental() instead. * If item streaming is enabled, call this method once all items have been delivered * using itemsRetrieved() or itemsRetrievedIncremental(). * @see retrieveItems() */ void itemsRetrievalDone(); /** * Call this method to remove all items and collections of the resource from the * server cache. * * The method should not be used anymore * * @see invalidateCache() * @since 4.3 */ void clearCache(); /** * Call this method to invalidate all cached content in @p collection. * * The method should be used when the backend indicated that the cached content * is no longer valid. * * @param collection parent of the content to be invalidated in cache * @since 4.8 */ void invalidateCache(const Collection &collection); /** * Returns the collection that is currently synchronized. * @note Calling this method is only allowed during a collection synchronization task, that * is directly or indirectly from retrieveItems(). */ Collection currentCollection() const; /** * Returns the item that is currently retrieved. * @note Calling this method is only allowed during fetching a single item, that * is directly or indirectly from retrieveItem(). */ AKONADIAGENTBASE_DEPRECATED Item currentItem() const; /** * Returns the items that are currently retrieved. * @note Calling this method is only allowed during item fetch, that is * directly or indirectly from retrieveItems(Akonadi::Item::List,QSet) */ Item::List currentItems() const; /** * This method is called whenever the resource should start synchronize all data. */ void synchronize(); /** * This method is called whenever the collection with the given @p id * shall be synchronized. */ void synchronizeCollection(qint64 id); /** * This method is called whenever the collection with the given @p id * shall be synchronized. * @param recursive if true, a recursive synchronization is done */ void synchronizeCollection(qint64 id, bool recursive); /** * This method is called whenever the collection with the given @p id * shall have its attributes synchronized. * * @param id The id of the collection to synchronize * @since 4.6 */ void synchronizeCollectionAttributes(qint64 id); /** * Synchronizes the collection attributes. * * @param col The id of the collection to synchronize * @since 4.15 */ void synchronizeCollectionAttributes(const Akonadi::Collection &col); /** * Refetches the Collections. */ void synchronizeCollectionTree(); /** * Refetches Tags. */ void synchronizeTags(); /** * Refetches Relations. */ void synchronizeRelations(); /** * Stops the execution of the current task and continues with the next one. */ void cancelTask(); /** * Stops the execution of the current task and continues with the next one. * Additionally an error message is emitted. * @param error additional error message to be emitted */ void cancelTask(const QString &error); /** * Stops the execution of the current task and continues with the next one. * The current task will be tried again later. * * This can be used to delay the task processing until the resource has reached a safe * state, e.g. login to a server succeeded. * * @note This does not change the order of tasks so if there is no task with higher priority * e.g. a custom task added with #Prepend the deferred task will be processed again. * * @since 4.3 */ void deferTask(); /** * Inherited from AgentBase. * * When going offline, the scheduler aborts the current task, so you should * do the same in your resource, if the task implementation is asynchronous. */ void doSetOnline(bool online) Q_DECL_OVERRIDE; /** * Indicate the use of hierarchical remote identifiers. * * This means that it is possible to have two different items with the same * remoteId in different Collections. * * This should be called in the resource constructor as needed. * * @param enable whether to enable use of hierarchical remote identifiers * @since 4.4 */ void setHierarchicalRemoteIdentifiersEnabled(bool enable); friend class ResourceScheduler; friend class ::ResourceState; /** * Describes the scheduling priority of a task that has been queued * for execution. * * @see scheduleCustomTask * @since 4.4 */ enum SchedulePriority { Prepend, ///< The task will be executed as soon as the current task has finished. AfterChangeReplay, ///< The task is scheduled after the last ChangeReplay task in the queue Append ///< The task will be executed after all tasks currently in the queue are finished }; /** * Schedules a custom task in the internal scheduler. It will be queued with * all other tasks such as change replays and retrieval requests and eventually * executed by calling the specified method. With the priority parameter the * time of execution of the Task can be influenced. @see SchedulePriority * @param receiver The object the slot should be called on. * @param method The name of the method (and only the name, not signature, not SLOT(...) macro), * that should be called to execute this task. The method has to be a slot and take a QVariant as * argument. * @param argument A QVariant argument passed to the method specified above. Use this to pass task * parameters. * @param priority Priority of the task. Use this to influence the position in * the execution queue. * @since 4.4 */ void scheduleCustomTask(QObject *receiver, const char *method, const QVariant &argument, SchedulePriority priority = Append); /** * Indicate that the current task is finished. Use this method from the slot called via scheduleCustomTaks(). * As with all the other callbacks, make sure to either call taskDone() or cancelTask()/deferTask() on all * exit paths, otherwise the resource will hang. * @since 4.4 */ void taskDone(); /** * Dump the contents of the current ChangeReplay * @since 4.8.1 */ QString dumpNotificationListToString() const; /** * Dumps memory usage information to stdout. * For now it outputs the result of glibc's mallinfo(). * This is useful to check if your memory problems are due to poor management by glibc. * Look for a high value on fsmblks when interpreting results. * man mallinfo for more details. * @since 4.11 */ void dumpMemoryInfo() const; /** * Returns a string with memory usage information. * @see dumpMemoryInfo() * * @since 4.11 */ QString dumpMemoryInfoToString() const; /** * Dump the state of the scheduler * @since 4.8.1 */ QString dumpSchedulerToString() const; private: static QString parseArguments(int argc, char **argv); static int init(ResourceBase *r); // dbus resource interface friend class ::Akonadi__ResourceAdaptor; QString requestItemDelivery(const QList &uids, const QByteArrayList &parts); private: Q_DECLARE_PRIVATE(ResourceBase) Q_PRIVATE_SLOT(d_func(), void slotAbortRequested()) Q_PRIVATE_SLOT(d_func(), void slotDeliveryDone(KJob *)) Q_PRIVATE_SLOT(d_func(), void slotCollectionSyncDone(KJob *)) Q_PRIVATE_SLOT(d_func(), void slotDeleteResourceCollection()) Q_PRIVATE_SLOT(d_func(), void slotDeleteResourceCollectionDone(KJob *)) Q_PRIVATE_SLOT(d_func(), void slotCollectionDeletionDone(KJob *)) Q_PRIVATE_SLOT(d_func(), void slotInvalidateCache(const Akonadi::Collection &)) Q_PRIVATE_SLOT(d_func(), void slotLocalListDone(KJob *)) Q_PRIVATE_SLOT(d_func(), void slotSynchronizeCollection(const Akonadi::Collection &)) Q_PRIVATE_SLOT(d_func(), void slotCollectionListDone(KJob *)) Q_PRIVATE_SLOT(d_func(), void slotSynchronizeCollectionAttributes(const Akonadi::Collection &)) Q_PRIVATE_SLOT(d_func(), void slotCollectionListForAttributesDone(KJob *)) Q_PRIVATE_SLOT(d_func(), void slotCollectionAttributesSyncDone(KJob *)) Q_PRIVATE_SLOT(d_func(), void slotItemSyncDone(KJob *)) Q_PRIVATE_SLOT(d_func(), void slotPercent(KJob *, unsigned long)) Q_PRIVATE_SLOT(d_func(), void slotDelayedEmitProgress()) Q_PRIVATE_SLOT(d_func(), void slotPrepareItemRetrieval(const Akonadi::Item &items)) Q_PRIVATE_SLOT(d_func(), void slotPrepareItemRetrievalResult(KJob *)) Q_PRIVATE_SLOT(d_func(), void slotPrepareItemsRetrieval(const QVector &items)) Q_PRIVATE_SLOT(d_func(), void slotPrepareItemsRetrievalResult(KJob *)) Q_PRIVATE_SLOT(d_func(), void changeCommittedResult(KJob *)) Q_PRIVATE_SLOT(d_func(), void slotSessionReconnected()) Q_PRIVATE_SLOT(d_func(), void slotRecursiveMoveReplay(RecursiveMover *)) Q_PRIVATE_SLOT(d_func(), void slotRecursiveMoveReplayResult(KJob *)) Q_PRIVATE_SLOT(d_func(), void slotTagSyncDone(KJob *)) Q_PRIVATE_SLOT(d_func(), void slotRelationSyncDone(KJob *job)) Q_PRIVATE_SLOT(d_func(), void slotSynchronizeTags()) Q_PRIVATE_SLOT(d_func(), void slotSynchronizeRelations()) Q_PRIVATE_SLOT(d_func(), void slotItemRetrievalCollectionFetchDone(KJob *)) Q_PRIVATE_SLOT(d_func(), void slotAttributeRetrievalCollectionFetchDone(KJob *)) }; } #ifndef AKONADI_RESOURCE_MAIN /** * Convenience Macro for the most common main() function for Akonadi resources. */ #define AKONADI_RESOURCE_MAIN( resourceClass ) \ int main( int argc, char **argv ) \ { \ return Akonadi::ResourceBase::init( argc, argv ); \ } #endif #endif diff --git a/src/core/entitycache_p.h b/src/core/entitycache_p.h index 1964c4eeb..c7acc5fb3 100644 --- a/src/core/entitycache_p.h +++ b/src/core/entitycache_p.h @@ -1,544 +1,544 @@ /* Copyright (c) 2009 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_ENTITYCACHE_P_H #define AKONADI_ENTITYCACHE_P_H #include "item.h" #include "itemfetchjob.h" #include "itemfetchscope.h" #include "collection.h" #include "collectionfetchjob.h" #include "collectionfetchscope.h" #include "tag.h" #include "tagfetchjob.h" #include "tagfetchscope.h" #include "session.h" #include "akonaditests_export.h" #include #include #include #include #include class KJob; Q_DECLARE_METATYPE(QList) namespace Akonadi { /** @internal QObject part of EntityCache. */ class AKONADI_TESTS_EXPORT EntityCacheBase : public QObject { Q_OBJECT public: explicit EntityCacheBase(Session *session, QObject *parent = nullptr); void setSession(Session *session); protected: Session *session; Q_SIGNALS: void dataAvailable(); private Q_SLOTS: virtual void processResult(KJob *job) = 0; }; template struct EntityCacheNode { EntityCacheNode() : pending(false) , invalid(false) { } EntityCacheNode(typename T::Id id) : entity(T(id)) , pending(true) , invalid(false) { } T entity; bool pending; bool invalid; }; /** * @internal * A in-memory FIFO cache for a small amount of Item or Collection objects. */ template class EntityCache : public EntityCacheBase { public: typedef FetchScope_ FetchScope; - explicit EntityCache(int maxCapacity, Session *session = 0, QObject *parent = nullptr) + explicit EntityCache(int maxCapacity, Session *session = nullptr, QObject *parent = nullptr) : EntityCacheBase(session, parent) , mCapacity(maxCapacity) { } ~EntityCache() { qDeleteAll(mCache); } /** Object is available in the cache and can be retrieved. */ bool isCached(typename T::Id id) const { EntityCacheNode *node = cacheNodeForId(id); return node && !node->pending; } /** Object has been requested but is not yet loaded into the cache or is already available. */ bool isRequested(typename T::Id id) const { return cacheNodeForId(id); } /** Returns the cached object if available, an empty instance otherwise. */ virtual T retrieve(typename T::Id id) const { EntityCacheNode *node = cacheNodeForId(id); if (node && !node->pending && !node->invalid) { return node->entity; } return T(); } /** Marks the cache entry as invalid, use in case the object has been deleted on the server. */ void invalidate(typename T::Id id) { EntityCacheNode *node = cacheNodeForId(id); if (node) { node->invalid = true; } } /** Triggers a re-fetching of a cache entry, use if it has changed on the server. */ void update(typename T::Id id, const FetchScope &scope) { EntityCacheNode *node = cacheNodeForId(id); if (node) { mCache.removeAll(node); if (node->pending) { request(id, scope); } delete node; } } /** Requests the object to be cached if it is not yet in the cache. @returns @c true if it was in the cache already. */ virtual bool ensureCached(typename T::Id id, const FetchScope &scope) { EntityCacheNode *node = cacheNodeForId(id); if (!node) { request(id, scope); return false; } return !node->pending; } /** Asks the cache to retrieve @p id. @p request is used as a token to indicate which request has been finished in the dataAvailable() signal. */ virtual void request(typename T::Id id, const FetchScope &scope) { Q_ASSERT(!isRequested(id)); shrinkCache(); EntityCacheNode *node = new EntityCacheNode(id); FetchJob *job = createFetchJob(id, scope); job->setProperty("EntityCacheNode", QVariant::fromValue(id)); connect(job, SIGNAL(result(KJob *)), SLOT(processResult(KJob *))); mCache.enqueue(node); } private: EntityCacheNode *cacheNodeForId(typename T::Id id) const { for (typename QQueue *>::const_iterator it = mCache.constBegin(), endIt = mCache.constEnd(); it != endIt; ++it) { if ((*it)->entity.id() == id) { return *it; } } return nullptr; } void processResult(KJob *job) Q_DECL_OVERRIDE { if (job->error()) { //This can happen if we have stale notifications for items that have already been removed } typename T::Id id = job->property("EntityCacheNode").template value(); EntityCacheNode *node = cacheNodeForId(id); if (!node) { return; // got replaced in the meantime } node->pending = false; extractResult(node, job); // make sure we find this node again if something went wrong here, // most likely the object got deleted from the server in the meantime if (node->entity.id() != id) { // TODO: Recursion guard? If this is called with non-existing ids, the if will never be true! node->entity.setId(id); node->invalid = true; } emit dataAvailable(); } void extractResult(EntityCacheNode *node, KJob *job) const; inline FetchJob *createFetchJob(typename T::Id id, const FetchScope &scope) { FetchJob *fetch = new FetchJob(T(id), session); fetch->setFetchScope(scope); return fetch; } /** Tries to reduce the cache size until at least one more object fits in. */ void shrinkCache() { while (mCache.size() >= mCapacity && !mCache.first()->pending) { delete mCache.dequeue(); } } private: QQueue *> mCache; int mCapacity; }; template<> inline void EntityCache::extractResult(EntityCacheNode *node, KJob *job) const { CollectionFetchJob *fetch = qobject_cast(job); Q_ASSERT(fetch); if (fetch->collections().isEmpty()) { node->entity = Collection(); } else { node->entity = fetch->collections().at(0); } } template<> inline void EntityCache::extractResult(EntityCacheNode *node, KJob *job) const { ItemFetchJob *fetch = qobject_cast(job); Q_ASSERT(fetch); if (fetch->items().isEmpty()) { node->entity = Item(); } else { node->entity = fetch->items().at(0); } } template<> inline void EntityCache::extractResult(EntityCacheNode *node, KJob *job) const { TagFetchJob *fetch = qobject_cast(job); Q_ASSERT(fetch); if (fetch->tags().isEmpty()) { node->entity = Tag(); } else { node->entity = fetch->tags().at(0); } } template<> inline CollectionFetchJob *EntityCache::createFetchJob(Collection::Id id, const CollectionFetchScope &scope) { CollectionFetchJob *fetch = new CollectionFetchJob(Collection(id), CollectionFetchJob::Base, session); fetch->setFetchScope(scope); return fetch; } typedef EntityCache CollectionCache; typedef EntityCache ItemCache; typedef EntityCache TagCache; template struct EntityListCacheNode { EntityListCacheNode() : pending(false) , invalid(false) { } EntityListCacheNode(typename T::Id id) : entity(id) , pending(true) , invalid(false) { } T entity; bool pending; bool invalid; }; template class EntityListCache : public EntityCacheBase { public: typedef FetchScope_ FetchScope; - explicit EntityListCache(int maxCapacity, Session *session = 0, QObject *parent = nullptr) + explicit EntityListCache(int maxCapacity, Session *session = nullptr, QObject *parent = nullptr) : EntityCacheBase(session, parent) , mCapacity(maxCapacity) { } ~EntityListCache() { qDeleteAll(mCache); } /** Returns the cached object if available, an empty instance otherwise. */ typename T::List retrieve(const QList &ids) const { typename T::List list; for (typename T::Id id : ids) { EntityListCacheNode *node = mCache.value(id); if (!node || node->pending || node->invalid) { return typename T::List(); } list << node->entity; } return list; } /** Requests the object to be cached if it is not yet in the cache. @returns @c true if it was in the cache already. */ bool ensureCached(const QList &ids, const FetchScope &scope) { QList toRequest; bool result = true; for (typename T::Id id : ids) { EntityListCacheNode *node = mCache.value(id); if (!node) { toRequest << id; continue; } if (node->pending) { result = false; } } if (!toRequest.isEmpty()) { request(toRequest, scope, ids); return false; } return result; } /** Marks the cache entry as invalid, use in case the object has been deleted on the server. */ void invalidate(const QList &ids) { for (typename T::Id id : ids) { EntityListCacheNode *node = mCache.value(id); if (node) { node->invalid = true; } } } /** Triggers a re-fetching of a cache entry, use if it has changed on the server. */ void update(const QList &ids, const FetchScope &scope) { QList toRequest; for (typename T::Id id : ids) { EntityListCacheNode *node = mCache.value(id); if (node) { mCache.remove(id); if (node->pending) { toRequest << id; } delete node; } } if (!toRequest.isEmpty()) { request(toRequest, scope); } } /** Asks the cache to retrieve @p id. @p request is used as a token to indicate which request has been finished in the dataAvailable() signal. */ void request(const QList &ids, const FetchScope &scope, const QList &preserveIds = QList()) { Q_ASSERT(isNotRequested(ids)); shrinkCache(preserveIds); for (typename T::Id id : ids) { EntityListCacheNode *node = new EntityListCacheNode(id); mCache.insert(id, node); } FetchJob *job = createFetchJob(ids, scope); job->setProperty("EntityListCacheIds", QVariant::fromValue>(ids)); connect(job, SIGNAL(result(KJob *)), SLOT(processResult(KJob *))); } bool isNotRequested(const QList &ids) const { for (typename T::Id id : ids) { if (mCache.contains(id)) { return false; } } return true; } /** Object is available in the cache and can be retrieved. */ bool isCached(const QList &ids) const { for (typename T::Id id : ids) { EntityListCacheNode *node = mCache.value(id); if (!node || node->pending) { return false; } } return true; } private: /** Tries to reduce the cache size until at least one more object fits in. */ void shrinkCache(const QList &preserveIds) { typename QHash *>::Iterator iter = mCache.begin(); while (iter != mCache.end() && mCache.size() >= mCapacity) { if (iter.value()->pending || preserveIds.contains(iter.key())) { ++iter; continue; } delete iter.value(); iter = mCache.erase(iter); } } inline FetchJob *createFetchJob(const QList &ids, const FetchScope &scope) { FetchJob *job = new FetchJob(ids, session); job->setFetchScope(scope); return job; } void processResult(KJob *job) Q_DECL_OVERRIDE { if (job->error()) { qWarning() << job->errorString(); } const QList ids = job->property("EntityListCacheIds").value>(); typename T::List entities; extractResults(job, entities); for (typename T::Id id : ids) { EntityListCacheNode *node = mCache.value(id); if (!node) { continue; // got replaced in the meantime } node->pending = false; T result; typename T::List::Iterator iter = entities.begin(); for (; iter != entities.end(); ++iter) { if ((*iter).id() == id) { result = *iter; entities.erase(iter); break; } } // make sure we find this node again if something went wrong here, // most likely the object got deleted from the server in the meantime if (!result.isValid()) { node->entity = T(id); node->invalid = true; } else { node->entity = result; } } emit dataAvailable(); } void extractResults(KJob *job, typename T::List &entities) const; private: QHash *> mCache; int mCapacity; }; template<> inline void EntityListCache::extractResults(KJob *job, Collection::List &collections) const { CollectionFetchJob *fetch = qobject_cast(job); Q_ASSERT(fetch); collections = fetch->collections(); } template<> inline void EntityListCache::extractResults(KJob *job, Item::List &items) const { ItemFetchJob *fetch = qobject_cast(job); Q_ASSERT(fetch); items = fetch->items(); } template<> inline void EntityListCache::extractResults(KJob *job, Tag::List &tags) const { TagFetchJob *fetch = qobject_cast(job); Q_ASSERT(fetch); tags = fetch->tags(); } template<> inline CollectionFetchJob *EntityListCache::createFetchJob(const QList &ids, const CollectionFetchScope &scope) { CollectionFetchJob *fetch = new CollectionFetchJob(ids, CollectionFetchJob::Base, session); fetch->setFetchScope(scope); return fetch; } typedef EntityListCache CollectionListCache; typedef EntityListCache ItemListCache; typedef EntityListCache TagListCache; } #endif diff --git a/src/core/pastehelper_p.h b/src/core/pastehelper_p.h index 0caf6fa44..f59fbaa05 100644 --- a/src/core/pastehelper_p.h +++ b/src/core/pastehelper_p.h @@ -1,72 +1,72 @@ /* Copyright (c) 2008 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_PASTEHELPER_P_H #define AKONADI_PASTEHELPER_P_H #include "akonadicore_export.h" #include "collection.h" class KJob; class QMimeData; namespace Akonadi { class Session; /** @internal Helper methods for pasting/droping content into a collection. @todo Use in item/collection models as well for dnd */ namespace PasteHelper { /** Check whether the given mime data can be pasted into the given collection. @param mimeData The pasted/dropped data. @param collection The collection to paste/drop into. */ AKONADICORE_EXPORT bool canPaste(const QMimeData *mimeData, const Collection &collection); /** Paste/drop the given mime data into the given collection. @param mimeData The pasted/dropped data. @param collection The target collection. @param copy Indicate whether this is a copy or a move. @returns The job performing the paste, 0 if there is nothing to paste. */ -AKONADICORE_EXPORT KJob *paste(const QMimeData *mimeData, const Collection &collection, bool copy = true, Session *session = 0); +AKONADICORE_EXPORT KJob *paste(const QMimeData *mimeData, const Collection &collection, bool copy = true, Session *session = nullptr); /** URI list paste/drop. @param mimeData The pasted/dropped data. @param collection The target collection. @param action The drop action (copy/move/link). @returns The job performing the paste, 0 if there is nothing to paste. */ -AKONADICORE_EXPORT KJob *pasteUriList(const QMimeData *mimeData, const Collection &collection, Qt::DropAction action, Session *session = 0); +AKONADICORE_EXPORT KJob *pasteUriList(const QMimeData *mimeData, const Collection &collection, Qt::DropAction action, Session *session = nullptr); } } #endif diff --git a/src/core/protocolhelper_p.h b/src/core/protocolhelper_p.h index b2a2f70ea..593404056 100644 --- a/src/core/protocolhelper_p.h +++ b/src/core/protocolhelper_p.h @@ -1,339 +1,339 @@ /* Copyright (c) 2008 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_PROTOCOLHELPER_P_H #define AKONADI_PROTOCOLHELPER_P_H #include "cachepolicy.h" #include "collection.h" #include "collectionutils.h" #include "item.h" #include "itemfetchscope.h" #include "sharedvaluepool_p.h" #include "tag.h" #include "private/imapparser_p.h" #include "private/protocol_p.h" #include "private/scope_p.h" #include "private/tristate_p.h" #include #include #include #include #include namespace Akonadi { struct ProtocolHelperValuePool { typedef Internal::SharedValuePool FlagPool; typedef Internal::SharedValuePool MimeTypePool; FlagPool flagPool; MimeTypePool mimeTypePool; QHash ancestorCollections; }; /** @internal Helper methods for converting between libakonadi objects and their protocol representation. @todo Add unit tests for this. @todo Use exceptions for a useful error handling */ class ProtocolHelper { public: /** Part namespaces. */ enum PartNamespace { PartGlobal, PartPayload, PartAttribute }; /** Parse a cache policy definition. @param policy The parsed cache policy. @returns Akonadi::CachePolicy */ static CachePolicy parseCachePolicy(const Protocol::CachePolicy &policy); /** Convert a cache policy object into its protocol representation. */ static Protocol::CachePolicy cachePolicyToProtocol(const CachePolicy &policy); /** Convert a ancestor chain from its protocol representation into an Item object. */ static void parseAncestors(const QVector &ancestors, Item *item); /** Convert a ancestor chain from its protocol representation into a Collection object. */ static void parseAncestors(const QVector &ancestors, Collection *collection); /** Convert a ancestor chain from its protocol representation into an Item object. This method allows to pass a @p valuePool which acts as cache, so ancestor paths for the same @p parentCollection don't have to be parsed twice. */ static void parseAncestorsCached(const QVector &ancestors, Item *item, Collection::Id parentCollection, - ProtocolHelperValuePool *valuePool = 0); + ProtocolHelperValuePool *valuePool = nullptr); /** Convert a ancestor chain from its protocol representation into an Collection object. This method allows to pass a @p valuePool which acts as cache, so ancestor paths for the same @p parentCollection don't have to be parsed twice. */ static void parseAncestorsCached(const QVector &ancestors, Collection *collection, Collection::Id parentCollection, - ProtocolHelperValuePool *valuePool = 0); + ProtocolHelperValuePool *valuePool = nullptr); /** Parse a collection description. @param data The input data. @param requireParent Whether or not we require a parent as part of the data. @returns The parsed collection */ static Collection parseCollection(const Protocol::FetchCollectionsResponse &data, bool requireParent = true); static void parseAttributes(const Protocol::Attributes &attributes, Item *item); static void parseAttributes(const Protocol::Attributes &attributes, Collection *collection); static void parseAttributes(const Protocol::Attributes &attributes, Tag *entity); static CollectionStatistics parseCollectionStatistics(const Protocol::FetchCollectionStatsResponse &stats); /** Convert attributes to their protocol representation. */ static Protocol::Attributes attributesToProtocol(const Item &item, bool ns = false); static Protocol::Attributes attributesToProtocol(const Collection &collection, bool ns = false); static Protocol::Attributes attributesToProtocol(const Tag &entity, bool ns = false); /** Encodes part label and namespace. */ static QByteArray encodePartIdentifier(PartNamespace ns, const QByteArray &label); /** Decode part label and namespace. */ static QByteArray decodePartIdentifier(const QByteArray &data, PartNamespace &ns); /** Converts the given set of items into a protocol representation. @throws A Akonadi::Exception if the item set contains items with missing/invalid identifiers. */ template class Container> static Scope entitySetToScope(const Container &_objects) { if (_objects.isEmpty()) { throw Exception("No objects specified"); } Container objects(_objects); using namespace std::placeholders; std::sort(objects.begin(), objects.end(), [](const T & a, const T & b) -> bool { return a.id() < b.id(); }); if (objects.at(0).isValid()) { QVector uids; uids.reserve(objects.size()); for (const T &object : objects) { uids << object.id(); } ImapSet set; set.add(uids); return Scope(set); } if (entitySetHasGID(_objects)) { return entitySetToGID(_objects); } if (!entitySetHasRemoteIdentifier(_objects, std::mem_fn(&T::remoteId))) { throw Exception("No remote identifier specified"); } // check if we have RIDs or HRIDs if (entitySetHasHRID(_objects)) { return hierarchicalRidToScope(objects.first()); } return entitySetToRemoteIdentifier(Scope::Rid, _objects, std::mem_fn(&T::remoteId)); } static Protocol::ScopeContext commandContextToProtocol(const Akonadi::Collection &collection, const Akonadi::Tag &tag, const Item::List &requestedItems); /** Converts the given object identifier into a protocol representation. @throws A Akonadi::Exception if the item set contains items with missing/invalid identifiers. */ template static Scope entityToScope(const T &object) { return entitySetToScope(QVector() << object); } /** Converts the given collection's hierarchical RID into a protocol representation. Assumes @p col has a valid hierarchical RID, so check that before! */ static Scope hierarchicalRidToScope(const Collection &col); /** Converts the HRID of the given item into an ASAP protocol representation. Assumes @p item has a valid HRID. */ static Scope hierarchicalRidToScope(const Item &item); static Scope hierarchicalRidToScope(const Tag &/*tag*/) { assert(false); return Scope(); } /** Converts a given ItemFetchScope object into a protocol representation. */ static Protocol::FetchScope itemFetchScopeToProtocol(const ItemFetchScope &fetchScope); /** Converts a given TagFetchScope object into a protocol representation. */ static QVector tagFetchScopeToProtocol(const TagFetchScope &fetchScope); /** Parses a single line from an item fetch job result into an Item object. */ - static Item parseItemFetchResult(const Protocol::FetchItemsResponse &data, ProtocolHelperValuePool *valuePool = 0); + static Item parseItemFetchResult(const Protocol::FetchItemsResponse &data, ProtocolHelperValuePool *valuePool = nullptr); static Tag parseTagFetchResult(const Protocol::FetchTagsResponse &data); static Relation parseRelationFetchResult(const Protocol::FetchRelationsResponse &data); static bool streamPayloadToFile(const QString &file, const QByteArray &data, QByteArray &error); static Akonadi::Tristate listPreference(const Collection::ListPreference pref); private: template class Container> inline static typename std::enable_if < !std::is_same::value, bool >::type entitySetHasGID(const Container &objects) { return entitySetHasRemoteIdentifier(objects, std::mem_fn(&T::gid)); } template class Container> inline static typename std::enable_if::value, bool>::type - entitySetHasGID(const Container &/*objects*/, int * /*dummy*/ = 0) + entitySetHasGID(const Container &/*objects*/, int * /*dummy*/ = nullptr) { return false; } template class Container> inline static typename std::enable_if < !std::is_same::value, Scope >::type entitySetToGID(const Container &objects) { return entitySetToRemoteIdentifier(Scope::Gid, objects, std::mem_fn(&T::gid)); } template class Container> inline static typename std::enable_if::value, Scope>::type - entitySetToGID(const Container &/*objects*/, int * /*dummy*/ = 0) + entitySetToGID(const Container &/*objects*/, int * /*dummy*/ = nullptr) { return Scope(); } template class Container, typename RIDFunc> inline static bool entitySetHasRemoteIdentifier(const Container &objects, const RIDFunc &ridFunc) { return std::find_if(objects.constBegin(), objects.constEnd(), [ = ](const T & obj) { return ridFunc(obj).isEmpty(); }) == objects.constEnd(); } template class Container, typename RIDFunc> inline static typename std::enable_if::value, Scope>::type entitySetToRemoteIdentifier(Scope::SelectionScope scope, const Container &objects, const RIDFunc &ridFunc) { QStringList rids; rids.reserve(objects.size()); std::transform(objects.cbegin(), objects.cend(), std::back_inserter(rids), [ = ](const T & obj) -> QString { return ridFunc(obj); }); return Scope(scope, rids); } template class Container, typename RIDFunc> inline static typename std::enable_if::value, Scope>::type - entitySetToRemoteIdentifier(Scope::SelectionScope scope, const Container &objects, const RIDFunc &ridFunc, int * /*dummy*/ = 0) + entitySetToRemoteIdentifier(Scope::SelectionScope scope, const Container &objects, const RIDFunc &ridFunc, int * /*dummy*/ = nullptr) { QStringList rids; rids.reserve(objects.size()); std::transform(objects.cbegin(), objects.cend(), std::back_inserter(rids), [ = ](const T & obj) -> QString { return QString::fromLatin1(ridFunc(obj)); }); return Scope(scope, rids); } template class Container> inline static typename std::enable_if < !std::is_same::value, bool >::type entitySetHasHRID(const Container &objects) { return objects.size() == 1 && std::find_if(objects.constBegin(), objects.constEnd(), [](const T & obj) -> bool { return !CollectionUtils::hasValidHierarchicalRID(obj); }) == objects.constEnd(); // ### HRID sets are not yet specified } template class Container> inline static typename std::enable_if::value, bool>::type - entitySetHasHRID(const Container &/*objects*/, int * /*dummy*/ = 0) + entitySetHasHRID(const Container &/*objects*/, int * /*dummy*/ = nullptr) { return false; } }; } #endif diff --git a/src/server/storage/datastore.h b/src/server/storage/datastore.h index ec26adb69..6de3963c5 100644 --- a/src/server/storage/datastore.h +++ b/src/server/storage/datastore.h @@ -1,372 +1,372 @@ /*************************************************************************** * Copyright (C) 2006 by Andreas Gungl * * * * This program 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 program 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 General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef DATASTORE_H #define DATASTORE_H #include #include #include #include #include class QSqlQuery; class QTimer; #include "entities.h" #include "notificationcollector.h" namespace Akonadi { namespace Server { class NotificationCollector; /** This class handles all the database access.

Database configuration

You can select between various database backends during runtime using the @c $HOME/.config/akonadi/akonadiserverrc configuration file. Example: @verbatim [%General] Driver=QMYSQL [QMYSQL_EMBEDDED] Name=akonadi Options=SERVER_DATADIR=/home/foo/.local/share/akonadi/db_data [QMYSQL] Name=akonadi Host=localhost User=foo Password=***** #Options=UNIX_SOCKET=/home/foo/.local/share/akonadi/socket-bar/mysql.socket StartServer=true ServerPath=/usr/sbin/mysqld [QSQLITE] Name=/home/foo/.local/share/akonadi/akonadi.db @endverbatim Use @c General/Driver to select the QSql driver to use for database access. The following drivers are currently supported, other might work but are untested: - QMYSQL - QMYSQL_EMBEDDED - QSQLITE The options for each driver are read from the corresponding group. The following options are supported, dependent on the driver not all of them might have an effect: - Name: Database name, for sqlite that's the file name of the database. - Host: Hostname of the database server - User: Username for the database server - Password: Password for the database server - Options: Additional options, format is driver-dependent - StartServer: Start the database locally just for Akonadi instead of using an existing one - ServerPath: Path to the server executable */ class DataStore : public QObject { Q_OBJECT public: /** Closes the database connection and destroys the DataStore object. */ virtual ~DataStore(); /** Opens the database connection. */ virtual void open(); /** Closes the database connection. */ virtual void close(); /** Initializes the database. Should be called during startup by the main thread. */ virtual bool init(); /** Per thread singleton. */ static DataStore *self(); /** * Returns whether per thread DataStore has been created. */ static bool hasDataStore(); /* --- ItemFlags ----------------------------------------------------- */ virtual bool setItemsFlags(const PimItem::List &items, const QVector &flags, bool *flagsChanged = nullptr, const Collection &col = Collection(), bool silent = false); - virtual bool appendItemsFlags(const PimItem::List &items, const QVector &flags, bool *flagsChanged = 0, + virtual bool appendItemsFlags(const PimItem::List &items, const QVector &flags, bool *flagsChanged = nullptr, bool checkIfExists = true, const Collection &col = Collection(), bool silent = false); - virtual bool removeItemsFlags(const PimItem::List &items, const QVector &flags, bool *tagsChanged = 0, + virtual bool removeItemsFlags(const PimItem::List &items, const QVector &flags, bool *tagsChanged = nullptr, const Collection &collection = Collection(), bool silent = false); /* --- ItemTags ----------------------------------------------------- */ virtual bool setItemsTags(const PimItem::List &items, const Tag::List &tags, bool *tagsChanged = nullptr, bool silent = false); virtual bool appendItemsTags(const PimItem::List &items, const Tag::List &tags, bool *tagsChanged = nullptr, bool checkIfExists = true, const Collection &col = Collection(), bool silent = false); virtual bool removeItemsTags(const PimItem::List &items, const Tag::List &tags, bool *tagsChanged = nullptr, bool silent = false); virtual bool removeTags(const Tag::List &tags, bool silent = false); /* --- ItemParts ----------------------------------------------------- */ virtual bool removeItemParts(const PimItem &item, const QSet &parts); // removes all payload parts for this item. virtual bool invalidateItemCache(const PimItem &item); /* --- Collection ------------------------------------------------------ */ virtual bool appendCollection(Collection &collection); /// removes the given collection and all its content virtual bool cleanupCollection(Collection &collection); /// same as the above but for database backends without working referential actions on foreign keys virtual bool cleanupCollection_slow(Collection &collection); /// moves the collection @p collection to @p newParent. virtual bool moveCollection(Collection &collection, const Collection &newParent); virtual bool appendMimeTypeForCollection(qint64 collectionId, const QStringList &mimeTypes); static QString collectionDelimiter() { return QStringLiteral("/"); } /** Determines the active cache policy for this Collection. The active cache policy is set in the corresponding Collection fields. */ virtual void activeCachePolicy(Collection &col); /// Returns all virtual collections the @p item is linked to QVector virtualCollections(const PimItem &item); QMap< Server::Entity::Id, QList< PimItem > > virtualCollections(const Akonadi::Server::PimItem::List &items); /* --- PimItem ------------------------------------------------------- */ virtual bool appendPimItem(QVector &parts, const QVector &flags, const MimeType &mimetype, const Collection &collection, const QDateTime &dateTime, const QString &remote_id, const QString &remoteRevision, const QString &gid, PimItem &pimItem); /** * Removes the pim item and all referenced data ( e.g. flags ) */ virtual bool cleanupPimItems(const PimItem::List &items); /** * Unhides the specified PimItem. Emits the itemAdded() notification as * the hidden flag is assumed to have been set by appendPimItem() before * pushing the item to the preprocessor chain. The hidden item had his * notifications disabled until now (so for the clients the "unhide" operation * is actually a new item arrival). * * This function does NOT verify if the item was *really* hidden: this is * responsibility of the caller. */ virtual bool unhidePimItem(PimItem &pimItem); /** * Unhides all the items which have the "hidden" flag set. * This function doesn't emit any notification about the items * being unhidden so it's meant to be called only in rare circumstances. * The most notable call to this function is at server startup * when we attempt to restore a clean state of the database. */ virtual bool unhideAllPimItems(); /* --- Collection attributes ------------------------------------------ */ virtual bool addCollectionAttribute(const Collection &col, const QByteArray &key, const QByteArray &value); /** * Removes the given collection attribute for @p col. * @throws HandlerException on database errors * @returns @c true if the attribute existed, @c false otherwise */ virtual bool removeCollectionAttribute(const Collection &col, const QByteArray &key); /* --- Helper functions ---------------------------------------------- */ /** Begins a transaction. No changes will be written to the database and no notification signal will be emitted unless you call commitTransaction(). @return @c true if successful. */ virtual bool beginTransaction(const QString &name); /** Reverts all changes within the current transaction. */ virtual bool rollbackTransaction(); /** Commits all changes within the current transaction and emits all collected notfication signals. If committing fails, the transaction will be rolled back. */ virtual bool commitTransaction(); /** Returns true if there is a transaction in progress. */ virtual bool inTransaction() const; /** Returns the notification collector of this DataStore object. Use this to listen to change notification signals. */ virtual NotificationCollector *notificationCollector(); /** Returns the QSqlDatabase object. Use this for generating queries yourself. Will [re-]open the database, if it is closed. */ QSqlDatabase database(); /** Sets the current session id. */ void setSessionId(const QByteArray &sessionId) { mSessionId = sessionId; } /** Returns if the database is currently open */ bool isOpened() const { return m_dbOpened; } Q_SIGNALS: /** Emitted if a transaction has been successfully committed. */ void transactionCommitted(); /** Emitted if a transaction has been aborted. */ void transactionRolledBack(); protected: /** Creates a new DataStore object and opens it. */ DataStore(); void debugLastDbError(const char *actionDescription) const; void debugLastQueryError(const QSqlQuery &query, const char *actionDescription) const; private: bool doAppendItemsFlag(const PimItem::List &items, const Flag &flag, const QSet &existing, const Collection &col, bool silent); bool doAppendItemsTag(const PimItem::List &items, const Tag &tag, const QSet &existing, const Collection &col, bool silent); /** Converts the given date/time to the database format, i.e. "YYYY-MM-DD HH:MM:SS". @param dateTime the date/time in UTC @return the date/time in database format @see dateTimeToQDateTime */ static QString dateTimeFromQDateTime(const QDateTime &dateTime); /** Converts the given date/time from database format to QDateTime. @param dateTime the date/time in database format @return the date/time as QDateTime @see dateTimeFromQDateTime */ static QDateTime dateTimeToQDateTime(const QByteArray &dateTime); /** * Adds the @p query to current transaction, so that it can be replayed in * case the transaction deadlocks or timeouts. * * When DataStore is not in transaction or SQLite is configured, this method * does nothing. * * All queries will automatically be removed when transaction is committed. * * This method should only be used by QueryBuilder. */ void addQueryToTransaction(const QSqlQuery &query, bool isBatch); /** * Tries to execute all queries from last transaction again. If any of the * queries fails, the entire transaction is rolled back and fails. * * This method can only be used by QueryBuilder when database rolls back * transaction due to deadlock or timeout. * * @return Returns an invalid query when error occurs, or the last replayed * query on success. */ QSqlQuery retryLastTransaction(bool rollbackFirst); private Q_SLOTS: void sendKeepAliveQuery(); protected: static QThreadStorage sInstances; QString m_connectionName; QSqlDatabase m_database; bool m_dbOpened; uint m_transactionLevel; QVector > m_transactionQueries; QByteArray mSessionId; NotificationCollector *mNotificationCollector; QTimer *m_keepAliveTimer; static bool s_hasForeignKeyConstraints; // Gives QueryBuilder access to addQueryToTransaction() and retryLastTransaction() friend class QueryBuilder; }; } // namespace Server } // namespace Akonadi #endif