From 0bcfc57f24adf8ce8dfb2fad33b294b5f0110a89 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 27 Nov 2014 16:42:33 +0100 Subject: Updated ClientAPI draft. --- client/clientapi.h | 242 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 190 insertions(+), 52 deletions(-) (limited to 'client/clientapi.h') diff --git a/client/clientapi.h b/client/clientapi.h index b035708..8924328 100644 --- a/client/clientapi.h +++ b/client/clientapi.h @@ -2,20 +2,45 @@ #include #include +#include +#include +#include "store/database.h" namespace ClientAPI { -template -class Set { +/** + * Standardized Domain Types + * + * The don't adhere to any standard and can be freely extended + * Their sole purpose is providing a standardized interface to access data. + * + * This is necessary to decouple resource-backends from application domain containers (otherwise each resource would have to provide a faceade for each application domain container). + * + * These types will be frequently modified (for every new feature that should be exposed to the any client) + */ +class AkonadiDomainType { + /* + * Each domain object needs to store the resource, identifier, revision triple so we can link back to the storage location. + */ + QString identifier; + QString resource; + qint64 revision; }; -template -class TreeSet : public Set { +class Event : public AkonadiDomainType { }; +class Todo : public AkonadiDomainType { -class DomainObject { +}; +class Calendar : public AkonadiDomainType { + +}; +class Mail : public AkonadiDomainType { + +}; +class Folder : public AkonadiDomainType { }; @@ -26,7 +51,7 @@ class DomainObject { * ** dummy domain object that is a wrapper? * ** domain adapter has an accessor for the domain object to hide subclassing */ -class DomainAdapter : public DomainObject { +class EventDomainAdapter : public Event { // virtual void setFoo(const QString &value) // { // mBuffer.setFoo(value); @@ -41,17 +66,86 @@ class DomainAdapter : public DomainObject { }; + +/** + * Query result set + * + * This should probably become part of a generic kasync library. + * + * Functional is nice because we don't have to store data in the emitter + * Non functional and storing may be the right thing because we want an in-memory representation of the set + * non-functional also allows us to batch move data across thread boundaries. + */ + +template +class ResultEmitter; + +/* + * The promise side for the result provider + */ +template +class ResultProvider { +public: + void add(const T &value) + { + //the handler will be called in the other thread, protect + mResultEmitter->addHandler(value); + } + + QSharedPointer > emitter() + { + mResultEmitter = QSharedPointer >(new ResultEmitter()); + return emitter; + } + +private: + QSharedPointer > mResultEmitter; +}; + +/* + * The future side for the client. + * + * It does not directly hold the state. + */ +template +class ResultEmitter { +public: + void onAdded(const std::function &handler); + // void onRemoved(const std::function &handler); + +private: + friend class SetSource; + std::function addHandler; + // std::function removeHandler; +}; + +// template +// class TreeSet : public Set { +// +// }; + + + /** * A query that matches a set of objects + * + * The query will have to be updated regularly similary to the domain objects. + * It probably also makes sense to have a domain specific part of the query, + * such as what properties we're interested in (necessary information for on-demand + * loading of data). */ class Query { public: + //Resources to search QSet resources() const { return QSet(); } }; + /** * Interface for the store facade + * + * All methods are synchronous. */ template class StoreFacade { @@ -59,14 +153,10 @@ public: virtual void create(const DomainType &domainObject) = 0; virtual void modify(const DomainType &domainObject) = 0; virtual void remove(const DomainType &domainObject) = 0; - virtual void load(const Query &query) = 0; + virtual void load(const Query &query, const std::function &resultCallback) = 0; }; -class ResourceImpl { - -}; - /** * Actual implementation of the store facade that is provided by the resource plugin. * @@ -74,48 +164,78 @@ class ResourceImpl { * * A resource must provide this facade for each domain type it knows. * => is reimplemented a lot + * => we should have a base implementation * * This interface should be executed in a thread so we can synchronously retrieve data from the store. + * + * TODO: perhaps we should also allow async access and leave the thread/non-thread decision up to the implementation? */ -template -class StoreFacadeImpl : public StoreFacade { +template +class StoreFacadeImpl : public StoreFacade { +}; + +template<> +class StoreFacadeImpl : public StoreFacade { public: - void create(const DomainType &domainObject) { + void create(const Event &domainObject) { //FIXME here we would need to cast to DomainAdapter //Do actual work + //transformFromDomainType(domainObject); + //Ideally we have an adapter + //getAdater(domainObject).buffer(); + //domainObject.key(); => The domain object needs to provide the id + //writeToDb(); } - void modify(const DomainType &domainObject) { + void modify(const Event &domainObject) { //Do actual work } - void remove(const DomainType &domainObject) { + void remove(const Event &domainObject) { //Do actual work } - Set load(Query) { - Set resultSet; - - //retrieve results from store and fill into result set - - resultSet << - - return resultSet; + class EventBuffer { + QString value; + }; + + static Event transformToDomainType(const EventBuffer &buffer) { + //We may want to avoid copies here + Event event; + // //Ideally we don't have to copy and can use an adaptor instead + // return DomainAdaptor + return event; + }; + + void load(const Query &query, const std::function &resultCallback) { + //retrieve buffers from storage + QList queryresult; + foreach(const EventBuffer &buffer, queryresult) { + resultCallback(transformToDomainType(buffer)); + } } private: + //Dummy implementation + class ResourceImpl {}; ResourceImpl resource; + Database mDb; }; /** * Facade factory that returns a store facade implementation, by loading a plugin and providing the relevant implementation. + * + * If we were to provide default implementations for certain capabilities. Here would be the place to do so. + * + * TODO: pluginmechansims for resources to provide their implementations. */ class FacadeFactory { public: template static StoreFacade getFacade(const QString &resource) { - if (resource == "resourceX") { + //TODO errorhandling in case the resource doesn't support the domain type + if (resource == "dummyresource") { return StoreFacadeImpl(); } return StoreFacadeImpl(); @@ -127,48 +247,66 @@ public: */ class Store { public: + /** + * Asynchronusly load a dataset + */ template - static Set load(Query) + static QSharedPointer > load(Query query) { - //Query all resources and aggregate results - //Query tells us in which resources we're interested - Set resultSet; - - //FIXME this should run in a thread. - //The result set is immediately returned and a "promise"/"resultprovider", - //is passed to the actual query. The resultset is threadsafe so the query thread can safely move data - //via the promise to the mainthread. - for(auto resource, query.resources()) { - auto facade = FacadeFactory::getFacade(resource); - resultSet += facade.load(query); - } - return resultSet; + QSharedPointer > resultSet(new ResultProvider); + + //Create a job that executes the search function. + //We must guarantee that the emitter is returned before the first result is emitted. + //The thread boundary handling is implemented in the result provider. + // QtConcurrent::run([provider, resultSet](){ + // // Query all resources and aggregate results + // // query tells us in which resources we're interested + // for(const auto &resource, query.resources()) { + // auto facade = FacadeFactory::getFacade(resource); + // facade.load(query, resultSet.add); + // } + // }); + return resultSet->emitter(); } - //Future load(id); => Set with single value - - template - static TreeSet loadTree(Query) - { + /** + * Asynchronusly load a dataset with tree structure information + */ + // template + // static TreeSet loadTree(Query) + // { - } + // } - //Sync methods for modifications + /** + * Create a new entity. + */ template - static void create(const DomainType &domainObject) { - auto facade = FacadeFactory::getFacade(domainObject.resource()); + static void create(const DomainType &domainObject, const QString &resourceIdentifier) { + //Potentially move to separate thread as well + auto facade = FacadeFactory::getFacade(resourceIdentifier); facade.create(domainObject); } + /** + * Modify an entity. + * + * This includes moving etc. since these are also simple settings on a property. + */ template - static void modify(const DomainType &domainObject) { - auto facade = FacadeFactory::getFacade(domainObject.resource()); + static void modify(const DomainType &domainObject, const QString &resourceIdentifier) { + //Potentially move to separate thread as well + auto facade = FacadeFactory::getFacade(resourceIdentifier); facade.modify(domainObject); } + /** + * Remove an entity. + */ template - static void remove(const DomainType &domainObject) { - auto facade = FacadeFactory::getFacade(domainObject.resource()); + static void remove(const DomainType &domainObject, const QString &resourceIdentifier) { + //Potentially move to separate thread as well + auto facade = FacadeFactory::getFacade(resourceIdentifier); facade.remove(domainObject); } }; -- cgit v1.2.3