diff options
Diffstat (limited to 'libraries/logic/resources/Resource.cpp')
-rw-r--r-- | libraries/logic/resources/Resource.cpp | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/libraries/logic/resources/Resource.cpp b/libraries/logic/resources/Resource.cpp new file mode 100644 index 00000000..e95675d7 --- /dev/null +++ b/libraries/logic/resources/Resource.cpp @@ -0,0 +1,155 @@ +#include "Resource.h" + +#include <QDebug> + +#include "ResourceObserver.h" +#include "ResourceHandler.h" + +// definition of static members of Resource +QMap<QString, std::function<std::shared_ptr<ResourceHandler>(const QString &)>> Resource::m_handlers; +QMap<QPair<int, int>, std::function<QVariant(QVariant)>> Resource::m_transfomers; +QMap<QString, std::weak_ptr<Resource>> Resource::m_resources; + +struct NullResourceResult {}; +Q_DECLARE_METATYPE(NullResourceResult) +class NullResourceHandler : public ResourceHandler +{ +public: + explicit NullResourceHandler() + { + setResult(QVariant::fromValue<NullResourceResult>(NullResourceResult())); + } +}; + +Resource::Resource(const QString &resource) + : m_resource(resource) +{ + if (!resource.isEmpty()) + { + // a valid resource identifier has the format <id>:<data> + Q_ASSERT(resource.contains(':')); + // "parse" the resource identifier into id and data + const QString resourceId = resource.left(resource.indexOf(':')); + const QString resourceData = resource.mid(resource.indexOf(':') + 1); + + // create and set up the handler + Q_ASSERT(m_handlers.contains(resourceId)); + m_handler = m_handlers.value(resourceId)(resourceData); + } + else + { + m_handler = std::make_shared<NullResourceHandler>(); + } + + Q_ASSERT(m_handler); + m_handler->init(m_handler); + m_handler->setResource(this); +} +Resource::~Resource() +{ + qDeleteAll(m_observers); +} + +Resource::Ptr Resource::create(const QString &resource, Ptr placeholder) +{ + const QString storageId = storageIdentifier(resource, placeholder); + + // do we already have a resource? even if m_resources contains it it might not be valid any longer (weak_ptr) + Resource::Ptr ptr = m_resources.contains(storageId) + ? m_resources.value(storageId).lock() + : nullptr; + // did we have one? and is it still valid? + if (!ptr) + { + /* We don't want Resource to have a public constructor, but std::make_shared needs it, + * so we create a subclass of Resource here that exposes the constructor as public. + * The alternative would be making the allocator for std::make_shared a friend, but it + * differs between different STL implementations, so that would be a pain. + */ + struct ConstructableResource : public Resource + { + explicit ConstructableResource(const QString &resource) + : Resource(resource) {} + }; + ptr = std::make_shared<ConstructableResource>(resource); + ptr->m_placeholder = placeholder; + m_resources.insert(storageId, ptr); + } + return ptr; +} + +Resource::Ptr Resource::applyTo(ResourceObserver *observer) +{ + m_observers.append(observer); + observer->setSource(shared_from_this()); // give the observer a shared_ptr for us so we don't get deleted + observer->resourceUpdated(); // ask the observer to poll us immediently, we might already have data + return shared_from_this(); // allow chaining +} +Resource::Ptr Resource::applyTo(QObject *target, const char *property) +{ + // the cast to ResourceObserver* is required to ensure the right overload gets choosen, + // since QObjectResourceObserver also inherits from QObject + return applyTo(static_cast<ResourceObserver *>(new QObjectResourceObserver(target, property))); +} + +QVariant Resource::getResourceInternal(const int typeId) const +{ + // no result (yet), but a placeholder? delegate to the placeholder. + if (m_handler->result().isNull() && m_placeholder) + { + return m_placeholder->getResourceInternal(typeId); + } + const QVariant variant = m_handler->result(); + const auto typePair = qMakePair(int(variant.type()), typeId); + + // do we have an explicit transformer? use it. + if (m_transfomers.contains(typePair)) + { + return m_transfomers.value(typePair)(variant); + } + else + { + // we do not have an explicit transformer, so we just pass the QVariant, which will automatically + // transform some types for us (different numbers to each other etc.) + return variant; + } +} + +void Resource::reportResult() +{ + for (ResourceObserver *observer : m_observers) + { + observer->resourceUpdated(); + } +} +void Resource::reportFailure(const QString &reason) +{ + for (ResourceObserver *observer : m_observers) + { + observer->setFailure(reason); + } +} +void Resource::reportProgress(const int progress) +{ + for (ResourceObserver *observer : m_observers) + { + observer->setProgress(progress); + } +} + +void Resource::notifyObserverDeleted(ResourceObserver *observer) +{ + m_observers.removeAll(observer); +} + +QString Resource::storageIdentifier(const QString &id, Resource::Ptr placeholder) +{ + if (placeholder) + { + return id + '#' + storageIdentifier(placeholder->m_resource, placeholder->m_placeholder); + } + else + { + return id; + } +} |