/* * Copyright (C) 2014 Christian Mollekopf * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #pragma once #include #include #include #include #include #include #include #include "query.h" #include "resultprovider.h" #include "applicationdomaintype.h" #include "facadefactory.h" #include "log.h" Q_DECLARE_METATYPE(std::shared_ptr); namespace async { //This should abstract if we execute from eventloop or in thread. //It supposed to allow the caller to finish the current method before executing the runner. void run(const std::function &runner); } namespace Akonadi2 { using namespace async; /** * Store interface used in the client API. */ class Store { private: static QList getResources(const QList &resourceFilter, const QByteArray &type); public: static QString storageLocation(); static QByteArray resourceName(const QByteArray &instanceIdentifier); /** * Asynchronusly load a dataset */ template static QSharedPointer > load(Query query) { auto resultSet = QSharedPointer >::create(); //Execute the search in a thread. //We must guarantee that the emitter is returned before the first result is emitted. //The result provider must be threadsafe. async::run([query, resultSet](){ QEventLoop eventLoop; resultSet->onDone([&eventLoop](){ eventLoop.quit(); }); // Query all resources and aggregate results KAsync::iterate(getResources(query.resources, ApplicationDomain::getTypeName())) .template each([query, resultSet](const QByteArray &resource, KAsync::Future &future) { if (auto facade = FacadeFactory::instance().getFacade(resourceName(resource), resource)) { facade->load(query, *resultSet).template then([&future](){future.setFinished();}).exec(); //Keep the facade alive for the lifetime of the resultSet. resultSet->setFacade(facade); } else { //Ignore the error and carry on future.setFinished(); } }).template then([query, resultSet]() { resultSet->initialResultSetComplete(); if (!query.liveQuery) { resultSet->complete(); } }).exec(); //Keep the thread alive until the result is ready if (!resultSet->isDone()) { eventLoop.exec(); } }); return resultSet->emitter(); } enum Roles { DomainObjectRole = Qt::UserRole + 1 //Must be the same as in ModelResult }; /** * Asynchronusly load a dataset with tree structure information */ template static QSharedPointer loadModel(Query query) { auto model = QSharedPointer >::create(query, query.requestedProperties.toList()); auto resultProvider = std::make_shared >(model); //Keep the resultprovider alive for as long as the model lives model->setProperty("resultProvider", QVariant::fromValue(std::shared_ptr(resultProvider))); // Query all resources and aggregate results KAsync::iterate(getResources(query.resources, ApplicationDomain::getTypeName())) .template each([query, resultProvider](const QByteArray &resource, KAsync::Future &future) { auto facade = FacadeFactory::instance().getFacade(resourceName(resource), resource); if (facade) { facade->load(query, *resultProvider).template then([&future](){future.setFinished();}).exec(); //Keep the facade alive for the lifetime of the resultSet. //FIXME this would have to become a list resultProvider->setFacade(facade); } else { //Ignore the error and carry on future.setFinished(); } }).template then([query, resultProvider]() { resultProvider->initialResultSetComplete(); if (!query.liveQuery) { resultProvider->complete(); } }).exec(); return model; } template static std::shared_ptr > getFacade(const QByteArray &resourceInstanceIdentifier) { if (auto facade = FacadeFactory::instance().getFacade(resourceName(resourceInstanceIdentifier), resourceInstanceIdentifier)) { return facade; } return std::make_shared >(); } /** * Create a new entity. */ template static KAsync::Job create(const DomainType &domainObject) { //Potentially move to separate thread as well auto facade = getFacade(domainObject.resourceInstanceIdentifier()); return facade->create(domainObject).template then([facade](){}, [](int errorCode, const QString &error) { Warning() << "Failed to create"; }); } /** * Modify an entity. * * This includes moving etc. since these are also simple settings on a property. */ template static KAsync::Job modify(const DomainType &domainObject) { //Potentially move to separate thread as well auto facade = getFacade(domainObject.resourceInstanceIdentifier()); return facade->modify(domainObject).template then([facade](){}, [](int errorCode, const QString &error) { Warning() << "Failed to modify"; }); } /** * Remove an entity. */ template static KAsync::Job remove(const DomainType &domainObject) { //Potentially move to separate thread as well auto facade = getFacade(domainObject.resourceInstanceIdentifier()); return facade->remove(domainObject).template then([facade](){}, [](int errorCode, const QString &error) { Warning() << "Failed to remove"; }); } /** * Shutdown resource. */ static KAsync::Job shutdown(const QByteArray &resourceIdentifier); /** * Synchronize data to local cache. */ static KAsync::Job synchronize(const Akonadi2::Query &query); }; }