summaryrefslogtreecommitdiffstats
path: root/api/logic/resources/Resource.cpp
blob: e95675d7cd536d06d69a20283c2a37537ca13b59 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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;
	}
}