File indexing completed on 2024-05-12 05:25:58

0001 /*
0002  *   Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm>
0003  *
0004  *   This program is free software; you can redistribute it and/or modify
0005  *   it under the terms of the GNU General Public License as published by
0006  *   the Free Software Foundation; either version 2 of the License, or
0007  *   (at your option) any later version.
0008  *
0009  *   This program is distributed in the hope that it will be useful,
0010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012  *   GNU General Public License for more details.
0013  *
0014  *   You should have received a copy of the GNU General Public License
0015  *   along with this program; if not, write to the
0016  *   Free Software Foundation, Inc.,
0017  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
0018  */
0019 
0020 #pragma once
0021 
0022 #include <QVariant>
0023 #include <QByteArray>
0024 #include <functional>
0025 
0026 #include "domaintypeadaptorfactoryinterface.h"
0027 #include "domain/applicationdomaintype.h"
0028 #include "domain/typeimplementations.h"
0029 #include "bufferadaptor.h"
0030 #include "entity_generated.h"
0031 #include "entitybuffer.h"
0032 #include "propertymapper.h"
0033 #include "log.h"
0034 
0035 /**
0036  * Create a buffer from a domain object using the provided mappings
0037  */
0038 template <class Builder, class Buffer>
0039 flatbuffers::Offset<Buffer>
0040 createBufferPart(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, const PropertyMapper &mapper)
0041 {
0042     // First create a primitives such as strings using the mappings
0043     QList<std::function<void(void *builder)>> propertiesToAddToResource;
0044     for (const auto &property : domainObject.changedProperties()) {
0045         // SinkTrace() << "copying property " << property;
0046         const auto value = domainObject.getProperty(property);
0047         if (mapper.hasMapping(property)) {
0048             mapper.setProperty(property, domainObject.getProperty(property), propertiesToAddToResource, fbb);
0049         } else {
0050             // SinkTrace() << "no mapping for property available " << property;
0051         }
0052     }
0053 
0054     // Then create all porperties using the above generated builderCalls
0055     Builder builder(fbb);
0056     for (auto propertyBuilder : propertiesToAddToResource) {
0057         propertyBuilder(&builder);
0058     }
0059     return builder.Finish();
0060 }
0061 
0062 /**
0063  * Create the buffer and finish the FlatBufferBuilder.
0064  *
0065  * After this the buffer can be extracted from the FlatBufferBuilder object.
0066  */
0067 template <typename Buffer, typename BufferBuilder>
0068 static void createBufferPartBuffer(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, PropertyMapper &mapper)
0069 {
0070     auto pos = createBufferPart<BufferBuilder, Buffer>(domainObject, fbb, mapper);
0071     // Because we cannot template the following call
0072     // Sink::ApplicationDomain::Buffer::FinishEventBuffer(fbb, pos);
0073     // FIXME: This means all buffers in here must have the AKFB identifier
0074     fbb.Finish(pos, "AKFB");
0075     flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
0076     if (!verifier.VerifyBuffer<Buffer>(nullptr)) {
0077         SinkWarning_(0, "bufferadaptor") << "Created invalid uffer";
0078     }
0079 }
0080 
0081 class IndexPropertyMapper
0082 {
0083 public:
0084     typedef std::function<QVariant(TypeIndex &index, const Sink::ApplicationDomain::BufferAdaptor &adaptor)> Accessor;
0085     virtual ~IndexPropertyMapper(){};
0086 
0087     virtual QVariant getProperty(const QByteArray &key, TypeIndex &index, const Sink::ApplicationDomain::BufferAdaptor &adaptor) const
0088     {
0089         auto accessor = mReadAccessors.value(key);
0090         Q_ASSERT(accessor);
0091         if (!accessor) {
0092             return QVariant();
0093         }
0094         return accessor(index, adaptor);
0095     }
0096 
0097     bool hasMapping(const QByteArray &key) const
0098     {
0099         return mReadAccessors.contains(key);
0100     }
0101 
0102     QList<QByteArray> availableProperties() const
0103     {
0104         return mReadAccessors.keys();
0105     }
0106 
0107     template<typename Property>
0108     void addIndexLookupProperty(const Accessor &accessor)
0109     {
0110         mReadAccessors.insert(Property::name, accessor);
0111     }
0112 
0113 private:
0114     QHash<QByteArray, Accessor> mReadAccessors;
0115 };
0116 
0117 /**
0118  * A generic adaptor implementation that uses a property mapper to read/write values.
0119  */
0120 class DatastoreBufferAdaptor : public Sink::ApplicationDomain::BufferAdaptor
0121 {
0122 public:
0123     DatastoreBufferAdaptor() : BufferAdaptor()
0124     {
0125     }
0126 
0127     virtual void setProperty(const QByteArray &key, const QVariant &value) Q_DECL_OVERRIDE
0128     {
0129         SinkWarning() << "Can't set property " << key;
0130         Q_ASSERT(false);
0131     }
0132 
0133     virtual QVariant getProperty(const QByteArray &key) const Q_DECL_OVERRIDE
0134     {
0135         if (mLocalBuffer && mLocalMapper->hasMapping(key)) {
0136             return mLocalMapper->getProperty(key, mLocalBuffer);
0137         } else if (mIndex && mIndexMapper->hasMapping(key)) {
0138             return mIndexMapper->getProperty(key, *mIndex, *this);
0139         }
0140         return QVariant();
0141     }
0142 
0143     /**
0144      * Returns all available properties for which a mapping exists (no matter what the buffer contains)
0145      */
0146     virtual QList<QByteArray> availableProperties() const Q_DECL_OVERRIDE
0147     {
0148         return mLocalMapper->availableProperties() + mIndexMapper->availableProperties();
0149     }
0150 
0151     void const *mLocalBuffer;
0152     QSharedPointer<PropertyMapper> mLocalMapper;
0153     QSharedPointer<IndexPropertyMapper> mIndexMapper;
0154     TypeIndex *mIndex;
0155 };
0156 
0157 /**
0158  * The factory should define how to go from an entitybuffer (local buffer), to a domain type adapter.
0159  * It defines how values are split accross local and resource buffer.
0160  * This is required by the facade the read the value, and by the pipeline preprocessors to access the domain values in a generic way.
0161  */
0162 template <typename DomainType>
0163 class DomainTypeAdaptorFactory : public DomainTypeAdaptorFactoryInterface
0164 {
0165     typedef typename Sink::ApplicationDomain::TypeImplementation<DomainType>::Buffer LocalBuffer;
0166     typedef typename Sink::ApplicationDomain::TypeImplementation<DomainType>::BufferBuilder LocalBuilder;
0167 
0168 public:
0169     DomainTypeAdaptorFactory()
0170         : mPropertyMapper(QSharedPointer<PropertyMapper>::create()),
0171           mIndexMapper(QSharedPointer<IndexPropertyMapper>::create())
0172     {
0173         Sink::ApplicationDomain::TypeImplementation<DomainType>::configure(*mPropertyMapper);
0174         Sink::ApplicationDomain::TypeImplementation<DomainType>::configure(*mIndexMapper);
0175     }
0176 
0177     virtual ~DomainTypeAdaptorFactory(){};
0178 
0179     /**
0180      * Creates an adaptor for the given domain types.
0181      *
0182      * This returns by default a DatastoreBufferAdaptor initialized with the corresponding property mappers.
0183      */
0184     virtual QSharedPointer<Sink::ApplicationDomain::BufferAdaptor> createAdaptor(const Sink::Entity &entity, TypeIndex *index = nullptr) Q_DECL_OVERRIDE
0185     {
0186         auto adaptor = QSharedPointer<DatastoreBufferAdaptor>::create();
0187         adaptor->mLocalBuffer = Sink::EntityBuffer::readBuffer<LocalBuffer>(entity.local());
0188         adaptor->mLocalMapper = mPropertyMapper;
0189         adaptor->mIndexMapper = mIndexMapper;
0190         adaptor->mIndex = index;
0191         return std::move(adaptor);
0192     }
0193 
0194     virtual bool
0195     createBuffer(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, void const *metadataData = 0, size_t metadataSize = 0) Q_DECL_OVERRIDE
0196     {
0197         flatbuffers::FlatBufferBuilder localFbb;
0198         createBufferPartBuffer<LocalBuffer, LocalBuilder>(domainObject, localFbb, *mPropertyMapper);
0199         Sink::EntityBuffer::assembleEntityBuffer(fbb, metadataData, metadataSize, 0, 0, localFbb.GetBufferPointer(), localFbb.GetSize());
0200         return true;
0201     }
0202 
0203     virtual bool createBuffer(const QSharedPointer<Sink::ApplicationDomain::BufferAdaptor> &bufferAdaptor, flatbuffers::FlatBufferBuilder &fbb, void const *metadataData = nullptr, size_t metadataSize = 0) Q_DECL_OVERRIDE
0204     {
0205         //TODO rewrite the unterlying functions so we don't have to wrap the bufferAdaptor
0206         auto  newObject = Sink::ApplicationDomain::ApplicationDomainType("", "", 0, bufferAdaptor);
0207         //Serialize all properties
0208         newObject.setChangedProperties(bufferAdaptor->availableProperties().toSet());
0209         return createBuffer(newObject, fbb, metadataData, metadataSize);
0210     }
0211 
0212 
0213 protected:
0214     QSharedPointer<PropertyMapper> mPropertyMapper;
0215     QSharedPointer<IndexPropertyMapper> mIndexMapper;
0216 };
0217 
0218 /**
0219  * A default adaptorfactory implemenation that simply instantiates a generic resource
0220  */
0221 template<typename DomainType>
0222 class DefaultAdaptorFactory : public DomainTypeAdaptorFactory<DomainType>
0223 {
0224 public:
0225     DefaultAdaptorFactory() : DomainTypeAdaptorFactory<DomainType>()  {}
0226     virtual ~DefaultAdaptorFactory(){}
0227 };
0228