diff --git a/src/blackmisc/sharedstate/listjournal.cpp b/src/blackmisc/sharedstate/listjournal.cpp new file mode 100644 index 000000000..3d1855b20 --- /dev/null +++ b/src/blackmisc/sharedstate/listjournal.cpp @@ -0,0 +1,40 @@ +/* Copyright (C) 2020 + * swift Project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, + * or distributed except according to the terms contained in the LICENSE file. + */ + +//! \file + +#include "blackmisc/sharedstate/listjournal.h" +#include "blackmisc/sharedstate/datalink.h" + +namespace BlackMisc +{ + namespace SharedState + { + void CGenericListJournal::initialize(IDataLink *dataLink) + { + dataLink->publish(m_mutator.data()); + dataLink->subscribe(m_observer.data()); + m_observer->setEventSubscription(CVariant::from(CAnyMatch())); + } + + CVariant CGenericListJournal::handleRequest(const CVariant &filter) + { + CVariantList copy = m_value; + if (filter.isValid()) + { + copy.removeIf([&filter](const CVariant &v) { return ! filter.matches(v); }); + } + return CVariant::from(copy); + } + + void CGenericListJournal::handleEvent(const CVariant ¶m) + { + m_value.push_back(param); + } + } +} diff --git a/src/blackmisc/sharedstate/listjournal.h b/src/blackmisc/sharedstate/listjournal.h new file mode 100644 index 000000000..8f5cb29d4 --- /dev/null +++ b/src/blackmisc/sharedstate/listjournal.h @@ -0,0 +1,65 @@ +/* Copyright (C) 2020 + * swift Project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, + * or distributed except according to the terms contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_SHAREDSTATE_LISTJOURNAL_H +#define BLACKMISC_SHAREDSTATE_LISTJOURNAL_H + +#include "blackmisc/sharedstate/activemutator.h" +#include "blackmisc/sharedstate/passiveobserver.h" +#include "blackmisc/variantlist.h" +#include "blackmisc/blackmiscexport.h" +#include +#include + +namespace BlackMisc +{ + namespace SharedState + { + class IDataLink; + + /*! + * Non-template base class for CListJournal. + */ + class BLACKMISC_EXPORT CGenericListJournal : public QObject + { + Q_OBJECT + + public: + //! Publish using the given transport mechanism. + void initialize(IDataLink *); + + protected: + //! Constructor. + CGenericListJournal(QObject *parent) : QObject(parent) {} + + private: + CVariant handleRequest(const CVariant &filter); + void handleEvent(const CVariant ¶m); + + QSharedPointer m_mutator = CActiveMutator::create(this, &CGenericListJournal::handleRequest); + QSharedPointer m_observer = CPassiveObserver::create(this, &CGenericListJournal::handleEvent); + CVariantList m_value; + }; + + /*! + * Base class for an object that shares state with a corresponding CListObserver subclass object. + * \tparam T Datatype encapsulating the state to be shared. + */ + template + class CListJournal : public CGenericListJournal + { + protected: + //! Constructor. + CListJournal(QObject *parent) : CGenericListJournal(parent) {} + }; + } +} + +#endif diff --git a/src/blackmisc/sharedstate/listmutator.cpp b/src/blackmisc/sharedstate/listmutator.cpp new file mode 100644 index 000000000..8b9b3caf8 --- /dev/null +++ b/src/blackmisc/sharedstate/listmutator.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2017 + * swift Project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, + * or distributed except according to the terms contained in the LICENSE file. + */ + +//! \file + +#include "blackmisc/sharedstate/listmutator.h" +#include "blackmisc/sharedstate/datalink.h" + +namespace BlackMisc +{ + namespace SharedState + { + void CGenericListMutator::initialize(IDataLink *dataLink) + { + dataLink->publish(m_mutator.data()); + } + + void CGenericListMutator::addElement(const CVariant &value) + { + m_mutator->postEvent(value); + } + } +} diff --git a/src/blackmisc/sharedstate/listmutator.h b/src/blackmisc/sharedstate/listmutator.h new file mode 100644 index 000000000..af20d8506 --- /dev/null +++ b/src/blackmisc/sharedstate/listmutator.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2017 + * swift Project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, + * or distributed except according to the terms contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_SHAREDSTATE_LISTMUTATOR_H +#define BLACKMISC_SHAREDSTATE_LISTMUTATOR_H + +#include "blackmisc/sharedstate/passivemutator.h" +#include "blackmisc/variantlist.h" +#include "blackmisc/blackmiscexport.h" +#include +#include + +namespace BlackMisc +{ + namespace SharedState + { + class IDataLink; + + /*! + * Non-template base class for CListMutator. + */ + class BLACKMISC_EXPORT CGenericListMutator : public QObject + { + Q_OBJECT + + public: + //! Publish using the given transport mechanism. + void initialize(IDataLink *); + + protected: + //! Constructor. + CGenericListMutator(QObject *parent) : QObject(parent) {} + + //! Add list element as variant. + void addElement(const CVariant &value); + + private: + QSharedPointer m_mutator = CPassiveMutator::create(this); + }; + + /*! + * Base class for an object that shares state with a corresponding CListObserver subclass object. + * \tparam T Datatype encapsulating the state to be shared. + */ + template + class CListMutator : public CGenericListMutator + { + protected: + //! Constructor. + CListMutator(QObject *parent) : CGenericListMutator(parent) {} + + public: + //! Add list element. + void addElement(const typename T::value_type &value) { CGenericListMutator::addElement(CVariant::from(value)); } + }; + } +} + +#endif diff --git a/src/blackmisc/sharedstate/listobserver.cpp b/src/blackmisc/sharedstate/listobserver.cpp new file mode 100644 index 000000000..46394802e --- /dev/null +++ b/src/blackmisc/sharedstate/listobserver.cpp @@ -0,0 +1,66 @@ +/* Copyright (C) 2017 + * swift Project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, + * or distributed except according to the terms contained in the LICENSE file. + */ + +//! \file + +#include "blackmisc/sharedstate/listobserver.h" +#include "blackmisc/sharedstate/datalink.h" +#include "blackmisc/variantlist.h" + +namespace BlackMisc +{ + namespace SharedState + { + void CGenericListObserver::initialize(IDataLink *dataLink) + { + dataLink->subscribe(m_observer.data()); + m_watcher = dataLink->watcher(); + connect(m_watcher, &CDataLinkConnectionWatcher::connected, this, &CGenericListObserver::reconstruct); + } + + void CGenericListObserver::setFilter(const CVariant &filter) + { + m_observer->setEventSubscription(filter); + if (m_watcher && m_watcher->isConnected()) { reconstruct(); } + } + + void CGenericListObserver::reconstruct() + { + m_observer->requestAsync(m_observer->eventSubscription(), [this](const CVariant &list) + { + QMutexLocker lock(&m_listMutex); + m_list = list.to(); + lock.unlock(); + onGenericElementsReplaced(allValues()); + }); + } + + CVariantList CGenericListObserver::allValues() const + { + QMutexLocker lock(&m_listMutex); + return m_list; + } + + int CGenericListObserver::cleanValues() + { + QMutexLocker lock(&m_listMutex); + return m_list.removeIf([filter = m_observer->eventSubscription()](const CVariant &value) + { + return !value.matches(filter); + }); + } + + void CGenericListObserver::handleEvent(const CVariant ¶m) + { + QMutexLocker lock(&m_listMutex); + m_list.push_back(param); + lock.unlock(); + onGenericElementAdded(param); + } + } +} diff --git a/src/blackmisc/sharedstate/listobserver.h b/src/blackmisc/sharedstate/listobserver.h new file mode 100644 index 000000000..59f42f2fe --- /dev/null +++ b/src/blackmisc/sharedstate/listobserver.h @@ -0,0 +1,99 @@ +/* Copyright (C) 2017 + * swift Project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, + * or distributed except according to the terms contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_SHAREDSTATE_LISTOBSERVER_H +#define BLACKMISC_SHAREDSTATE_LISTOBSERVER_H + +#include "blackmisc/sharedstate/activeobserver.h" +#include "blackmisc/sharedstate/datalink.h" +#include "blackmisc/variantlist.h" +#include "blackmisc/blackmiscexport.h" +#include +#include + +namespace BlackMisc +{ + namespace SharedState + { + /*! + * Non-template base class for CListObserver. + */ + class BLACKMISC_EXPORT CGenericListObserver : public QObject + { + Q_OBJECT + + protected: + //! Constructor. + CGenericListObserver(QObject *parent) : QObject(parent) {} + + //! Subscribe using the given transport mechanism. + virtual void initialize(IDataLink *); + + //! Set list filter as variant. + void setFilter(const CVariant &filter); + + //! Get filtered list value as variant list. + CVariantList allValues() const; + + //! Remove any old values that no longer match the filter. + int cleanValues(); + + private: + void reconstruct(); + void handleEvent(const CVariant ¶m); + virtual void onGenericElementAdded(const CVariant &value) = 0; + virtual void onGenericElementsReplaced(const CVariantList &values) = 0; + + QSharedPointer m_observer = CActiveObserver::create(this, &CGenericListObserver::handleEvent); + CDataLinkConnectionWatcher *m_watcher = nullptr; + mutable QMutex m_listMutex; + CVariantList m_list; + }; + + /*! + * Base class for an object that shares state with a corresponding CListMutator subclass object. + * \tparam T Datatype encapsulating the state to be shared. + * \tparam U Datatype describing a filter to apply to the list. + */ + template + class CListObserver : public CGenericListObserver + { + protected: + //! Constructor. + CListObserver(QObject *parent) : CGenericListObserver(parent) {} + + public: + //! Subscribe using the given transport mechanism. + virtual void initialize(IDataLink *dataLink) override + { + CGenericListObserver::initialize(dataLink); + if (std::is_same::value) { setFilter({}); } + } + + //! Set filter to choose list elements. + void setFilter(const U &filter) { CGenericListObserver::setFilter(CVariant::from(filter)); } + + //! Get list value containing all elements matching the filter. + T allValues() const { return CVariant::from(CGenericListObserver::allValues()).template to(); } + + //! Called when an element matching the filter is added to the list. + virtual void onElementAdded(const typename T::value_type &value) = 0; + + //! Called when the whole list is updated wholesale. + virtual void onElementsReplaced(const T &values) = 0; + + private: + virtual void onGenericElementAdded(const CVariant &value) override final { onElementAdded(value.to()); } + virtual void onGenericElementsReplaced(const CVariantList &values) override final { onElementsReplaced(values.to()); } + }; + } +} + +#endif