2015-05-28 18:38:29 +01:00
|
|
|
#include "Resource.h"
|
|
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
|
|
#include "ResourceObserver.h"
|
2015-09-26 12:45:29 +01:00
|
|
|
#include "ResourceHandler.h"
|
2015-05-28 18:38:29 +01:00
|
|
|
|
2015-06-06 11:30:49 +01:00
|
|
|
// definition of static members of Resource
|
2015-05-28 18:38:29 +01:00
|
|
|
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;
|
|
|
|
|
2015-09-26 12:45:29 +01:00
|
|
|
struct NullResourceResult {};
|
|
|
|
Q_DECLARE_METATYPE(NullResourceResult)
|
|
|
|
class NullResourceHandler : public ResourceHandler
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit NullResourceHandler()
|
|
|
|
{
|
|
|
|
setResult(QVariant::fromValue<NullResourceResult>(NullResourceResult()));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-05-28 18:38:29 +01:00
|
|
|
Resource::Resource(const QString &resource)
|
2015-06-06 11:30:49 +01:00
|
|
|
: m_resource(resource)
|
2015-05-28 18:38:29 +01:00
|
|
|
{
|
2015-09-26 12:45:29 +01:00
|
|
|
if (!resource.isEmpty())
|
2015-05-28 18:38:29 +01:00
|
|
|
{
|
2015-09-26 12:45:29 +01:00
|
|
|
// 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);
|
2015-05-28 18:38:29 +01:00
|
|
|
}
|
2015-09-26 12:45:29 +01:00
|
|
|
else
|
2015-05-28 18:38:29 +01:00
|
|
|
{
|
2015-09-26 12:45:29 +01:00
|
|
|
m_handler = std::make_shared<NullResourceHandler>();
|
2015-05-28 18:38:29 +01:00
|
|
|
}
|
|
|
|
|
2015-06-06 11:30:49 +01:00
|
|
|
Q_ASSERT(m_handler);
|
2015-05-28 18:38:29 +01:00
|
|
|
m_handler->init(m_handler);
|
|
|
|
m_handler->setResource(this);
|
|
|
|
}
|
|
|
|
Resource::~Resource()
|
|
|
|
{
|
|
|
|
qDeleteAll(m_observers);
|
|
|
|
}
|
|
|
|
|
2015-06-06 11:30:49 +01:00
|
|
|
Resource::Ptr Resource::create(const QString &resource, Ptr placeholder)
|
2015-05-28 18:38:29 +01:00
|
|
|
{
|
2015-06-06 11:30:49 +01:00
|
|
|
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()
|
2015-05-28 18:38:29 +01:00
|
|
|
: nullptr;
|
2015-06-06 11:30:49 +01:00
|
|
|
// did we have one? and is it still valid?
|
2015-05-28 18:38:29 +01:00
|
|
|
if (!ptr)
|
|
|
|
{
|
2015-06-06 11:30:49 +01:00
|
|
|
/* 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.
|
|
|
|
*/
|
2015-05-28 18:38:29 +01:00
|
|
|
struct ConstructableResource : public Resource
|
|
|
|
{
|
|
|
|
explicit ConstructableResource(const QString &resource)
|
|
|
|
: Resource(resource) {}
|
|
|
|
};
|
|
|
|
ptr = std::make_shared<ConstructableResource>(resource);
|
2015-06-06 11:30:49 +01:00
|
|
|
ptr->m_placeholder = placeholder;
|
|
|
|
m_resources.insert(storageId, ptr);
|
2015-05-28 18:38:29 +01:00
|
|
|
}
|
|
|
|
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
|
2015-06-06 11:30:49 +01:00
|
|
|
observer->resourceUpdated(); // ask the observer to poll us immediently, we might already have data
|
|
|
|
return shared_from_this(); // allow chaining
|
2015-05-28 18:38:29 +01:00
|
|
|
}
|
|
|
|
Resource::Ptr Resource::applyTo(QObject *target, const char *property)
|
|
|
|
{
|
2015-06-06 11:30:49 +01:00
|
|
|
// the cast to ResourceObserver* is required to ensure the right overload gets choosen,
|
|
|
|
// since QObjectResourceObserver also inherits from QObject
|
2015-05-28 18:38:29 +01:00
|
|
|
return applyTo(static_cast<ResourceObserver *>(new QObjectResourceObserver(target, property)));
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant Resource::getResourceInternal(const int typeId) const
|
|
|
|
{
|
2015-06-06 11:30:49 +01:00
|
|
|
// no result (yet), but a placeholder? delegate to the placeholder.
|
2015-05-28 18:38:29 +01:00
|
|
|
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);
|
2015-06-06 11:30:49 +01:00
|
|
|
|
|
|
|
// do we have an explicit transformer? use it.
|
2015-05-28 18:38:29 +01:00
|
|
|
if (m_transfomers.contains(typePair))
|
|
|
|
{
|
|
|
|
return m_transfomers.value(typePair)(variant);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-06-06 11:30:49 +01:00
|
|
|
// 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.)
|
2015-05-28 18:38:29 +01:00
|
|
|
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);
|
|
|
|
}
|
2015-06-06 11:30:49 +01:00
|
|
|
|
|
|
|
QString Resource::storageIdentifier(const QString &id, Resource::Ptr placeholder)
|
|
|
|
{
|
|
|
|
if (placeholder)
|
|
|
|
{
|
|
|
|
return id + '#' + storageIdentifier(placeholder->m_resource, placeholder->m_placeholder);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
}
|