Krita Source Code Documentation
Loading...
Searching...
No Matches
KisMemoryStorage.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2018 Boudewijn Rempt <boud@valdyas.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7#include "KisMemoryStorage.h"
8
9#include <optional>
10#include <QVector>
11
12#include <KisMimeDatabase.h>
13#include <kis_debug.h>
14#include <KisTag.h>
15#include <KisResourceStorage.h>
16#include <QBuffer>
18#include <kis_pointer_utils.h>
19#include <KoMD5Generator.h>
20#include <kis_assert.h>
21#include <KisMpl.h>
22
23
24namespace detail {
25
30 std::optional<std::pair<QString, QString>> splitResourceUrl(const QString &url)
31 {
32 if (!url.contains('/')) return std::nullopt;
33
34 QStringList parts = url.split('/', Qt::SkipEmptyParts);
35
36 if (parts.isEmpty()) return std::nullopt;
37
38 const QString resourceType = parts[0];
39 parts.removeFirst();
40 const QString resourceFilename = parts.join('/');
41 return std::make_pair(resourceType, resourceFilename);
42 }
43
44}
45
52
54{
55public:
57 : m_it(tags)
58 {
59 }
60
61 bool hasNext() const override
62 {
63 return m_it.hasNext();
64 }
65
66 void next() override
67 {
68 m_it.next();
69 }
70
71 KisTagSP tag() const override
72 {
73 return m_it.peekPrevious();
74 }
75
76private:
77 QVectorIterator<KisTagSP> m_it;
78};
79
80
82{
83public:
84 ~MemoryItem() override {}
85};
86
87
89public:
90 QHash<QString, QHash<QString, StoredResource>> resourcesNew;
91 QHash<QString, QVector<KisTagSP>> tags;
92 QMap<QString, QVariant> metadata;
93};
94
95
96KisMemoryStorage::KisMemoryStorage(const QString &location)
97 : KisStoragePlugin(location)
98 , d(new Private)
99{
100}
101
105
107 : KisStoragePlugin(rhs.location())
108 , d(new Private)
109{
110 *this = rhs;
111 d->resourcesNew = rhs.d->resourcesNew;
112 d->tags = rhs.d->tags;
113 d->metadata = rhs.d->metadata;
114}
115
117{
118 if (this != &rhs) {
119 d->resourcesNew = rhs.d->resourcesNew;
120
121 Q_FOREACH(const QString &key, rhs.d->tags.keys()) {
122 Q_FOREACH(const KisTagSP tag, rhs.d->tags[key]) {
123 if (!d->tags.contains(key)) {
124 d->tags[key] = QVector<KisTagSP>();
125 }
126 d->tags[key] << tag->clone();
127 }
128 }
129 }
130 return *this;
131}
132
133bool KisMemoryStorage::saveAsNewVersion(const QString &resourceType, KoResourceSP resource)
134{
135 QHash<QString, StoredResource> &typedResources =
136 d->resourcesNew[resourceType];
137
138 auto checkExists =
139 [&typedResources] (const QString &filename) {
140 return typedResources.contains(filename);
141 };
142
143 const QString newFilename =
145
146 if (newFilename.isEmpty()) return false;
147
148 resource->setFilename(newFilename);
149
150 StoredResource storedResource;
151 storedResource.timestamp = QDateTime::currentDateTime();
152 storedResource.data.reset(new QByteArray());
153 QBuffer buffer(storedResource.data.data());
154 buffer.open(QIODevice::WriteOnly);
155 bool result = resource->saveToDevice(&buffer);
156 buffer.close();
157 if (!result) {
158 storedResource.resource = resource;
159 }
160
161 typedResources.insert(newFilename, storedResource);
162
163 return true;
164}
165
167{
168 MemoryItem item;
169 item.url = url;
170 item.folder = QString();
171 item.lastModified = QDateTime::fromMSecsSinceEpoch(0);
172 return item;
173}
174
176{
177 const QString resourceType = resource->resourceType().first;
178 const QString resourceFilename = resource->filename();
179
180 bool retval = false;
181
182 if (d->resourcesNew.contains(resourceType) &&
183 d->resourcesNew[resourceType].contains(resourceFilename)) {
184
185 const StoredResource &storedResource =
186 d->resourcesNew[resourceType][resourceFilename];
187
188 if (storedResource.data->size() > 0) {
189 QBuffer buffer(storedResource.data.data());
190 buffer.open(QIODevice::ReadOnly);
191 resource->loadFromDevice(&buffer, KisGlobalResourcesInterface::instance());
192 } else {
193 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(storedResource.data->size() > 0, false);
194 qWarning() << "Cannot load resource from device in KisMemoryStorage::loadVersionedResource";
195 return false;
196 }
197 retval = true;
198 }
199
200 return retval;
201}
202
203bool KisMemoryStorage::importResource(const QString &url, QIODevice *device)
204{
205 auto parsedUrl = detail::splitResourceUrl(url);
206 if (!parsedUrl) return false;
207 auto [resourceType, resourceFilename] = *parsedUrl;
208
209 // we cannot overwrite existing file by API convention
210 if (d->resourcesNew.contains(resourceType) &&
211 d->resourcesNew[resourceType].contains(resourceFilename)) {
212 return false;
213 }
214
215 StoredResource storedResource;
216 storedResource.timestamp = QDateTime::currentDateTime();
217 storedResource.data.reset(new QByteArray(device->readAll()));
218
219 QHash<QString, StoredResource> &typedResources =
220 d->resourcesNew[resourceType];
221 typedResources.insert(resourceFilename, storedResource);
222
223 return true;
224}
225
226bool KisMemoryStorage::exportResource(const QString &url, QIODevice *device)
227{
228 auto parsedUrl = detail::splitResourceUrl(url);
229 if (!parsedUrl) return false;
230 auto [resourceType, resourceFilename] = *parsedUrl;
231
232 if (!d->resourcesNew.contains(resourceType) ||
233 !d->resourcesNew[resourceType].contains(resourceFilename)) {
234 return false;
235 }
236
237 const StoredResource &storedResource =
238 d->resourcesNew[resourceType][resourceFilename];
239
240 if (!storedResource.data) {
241 qWarning() << "Stored resource doesn't have a serialized representation!";
242 return false;
243 }
244
245 device->write(*storedResource.data);
246 return true;
247}
248
249bool KisMemoryStorage::addResource(const QString &resourceType, KoResourceSP resource)
250{
251 QHash<QString, StoredResource> &typedResources = d->resourcesNew[resourceType];
252
253 if (typedResources.contains(resource->filename())) {
257 return true;
258 };
259
260 StoredResource storedResource;
261 storedResource.timestamp = QDateTime::currentDateTime();
262 storedResource.data.reset(new QByteArray());
263 if (resource->isSerializable()) {
264 QBuffer buffer(storedResource.data.data());
265 buffer.open(QIODevice::WriteOnly);
266 if (!resource->saveToDevice(&buffer)) {
267 storedResource.resource = resource;
268 }
269 buffer.close();
270 } else {
271 storedResource.resource = resource;
272 }
273
274 typedResources.insert(resource->filename(), storedResource);
275
276 return true;
277}
278
280{
281 auto parsedUrl = detail::splitResourceUrl(url);
282 if (!parsedUrl) return false;
283 auto [resourceType, resourceFilename] = *parsedUrl;
284
285 if (d->resourcesNew.contains(resourceType)) {
286 return d->resourcesNew[resourceType].remove(resourceFilename) > 0;
287 }
288
289 return false;
290}
291
292bool KisMemoryStorage::testingAddTag(const QString &resourceType, KisTagSP tag)
293{
294 KIS_SAFE_ASSERT_RECOVER_NOOP(resourceType == tag->resourceType());
295
296 QVector<KisTagSP> &typedTags = d->tags[resourceType];
297
298 auto existingIt = std::find_if(typedTags.begin(), typedTags.end(), kismpl::mem_equal_to(&KisTag::url, tag->url()));
299 if (existingIt != typedTags.end()) {
300 typedTags.erase(existingIt);
301 }
302
303 typedTags.append(tag);
304
305 return true;
306}
307
308bool KisMemoryStorage::testingRemoveTag(const QString &resourceType, const QString &tagUrl)
309{
310 QVector<KisTagSP> &typedTags = d->tags[resourceType];
311
312 auto existingIt = std::find_if(typedTags.begin(), typedTags.end(), kismpl::mem_equal_to(&KisTag::url, tagUrl));
313 if (existingIt != typedTags.end()) {
314 typedTags.erase(existingIt);
315 return true;
316 }
317
318 return false;
319}
320
321QString KisMemoryStorage::resourceMd5(const QString &url)
322{
323 auto parsedUrl = detail::splitResourceUrl(url);
324 if (!parsedUrl) return QString();
325 auto [resourceType, resourceFilename] = *parsedUrl;
326
327 QString result;
328
329 if (d->resourcesNew.contains(resourceType) &&
330 d->resourcesNew[resourceType].contains(resourceFilename)) {
331
332 const StoredResource &storedResource =
333 d->resourcesNew[resourceType][resourceFilename];
334
335 if (storedResource.data->size() > 0 || storedResource.resource.isNull()) {
336 result = KoMD5Generator::generateHash(*storedResource.data);
337 } else {
338 result = storedResource.resource->md5Sum();
339 }
340 }
341
342 return result;
343}
344
346{
348
349
350 QHash<QString, StoredResource> &typedResources =
351 d->resourcesNew[resourceType];
352
353 for (auto it = typedResources.begin(); it != typedResources.end(); ++it) {
355 entry.filename = it.key();
356 entry.lastModified = it.value().timestamp;
357 entry.tagList = {}; // TODO
358 entry.resourceType = resourceType;
359 entries.append(entry);
360
361 }
362
364
365 return toQShared(new KisVersionedStorageIterator(entries, this));
366}
367
372
373void KisMemoryStorage::setMetaData(const QString &key, const QVariant &value)
374{
375 d->metadata[key] = value;
376}
377
379{
380 QStringList keys = d->metadata.keys();
381
382 if (!keys.contains(KisResourceStorage::s_meta_name)) {
384 }
385
386 return keys;
387}
388
389QVariant KisMemoryStorage::metaData(const QString &key) const
390{
391 QVariant r;
392 if (d->metadata.contains(key)) {
393 r = d->metadata[key];
394 }
395 return r;
396}
float value(const T *src, size_t ch)
static KisResourcesInterfaceSP instance()
QHash< QString, QHash< QString, StoredResource > > resourcesNew
QMap< QString, QVariant > metadata
QHash< QString, QVector< KisTagSP > > tags
The KisMemoryStorage class stores the temporary resources that are not saved to disk or bundle....
QSharedPointer< KisResourceStorage::TagIterator > tags(const QString &resourceType) override
bool saveAsNewVersion(const QString &resourceType, KoResourceSP resource) override
bool exportResource(const QString &url, QIODevice *device) override
bool importResource(const QString &url, QIODevice *device) override
void setMetaData(const QString &key, const QVariant &value) override
KisMemoryStorage & operator=(const KisMemoryStorage &rhs)
This clones all contained resources and tags from rhs.
KisMemoryStorage(const QString &location=QString("memory"))
bool loadVersionedResource(KoResourceSP resource) override
bool testingRemoveResource(const QString &url)
KisResourceStorage::ResourceItem resourceItem(const QString &url) override
bool testingAddTag(const QString &resourceType, KisTagSP tag)
QSharedPointer< KisResourceStorage::ResourceIterator > resources(const QString &resourceType) override
bool addResource(const QString &resourceType, KoResourceSP resource) override
QStringList metaDataKeys() const override
QString resourceMd5(const QString &url) override
bool testingRemoveTag(const QString &resourceType, const QString &tagUrl)
QVariant metaData(const QString &key) const override
QScopedPointer< Private > d
static const QString s_meta_name
virtual KoResourceSP resource(const QString &url)
static void detectFileVersions(QVector< VersionedResourceEntry > &allFiles)
static QString chooseUniqueName(KoResourceSP resource, int minVersion, std::function< bool(QString)> checkExists)
QString url() const
The unique identifier for the tag. Since tag urls are compared COLLATE NOCASE, tag urls must be ASCII...
Definition KisTag.cpp:159
static QString generateHash(const QString &filename)
generateHash reads the given file and generates a hex-encoded md5sum for the file.
~MemoryItem() override
QVectorIterator< KisTagSP > m_it
bool hasNext() const override
void next() override
The iterator is only valid if next() has been called at least once.
KisTagSP tag() const override
A tag object on which we can set properties and which we can save.
MemoryTagIterator(const QVector< KisTagSP > &tags)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
QSharedPointer< T > toQShared(T *ptr)
std::optional< std::pair< QString, QString > > splitResourceUrl(const QString &url)
auto mem_equal_to(MemTypeNoRef Class::*ptr, MemType &&value)
mem_equal_to is an unary functor that compares a member of the object to a given value
Definition KisMpl.h:233
A resource item is simply an entry in the storage,.
KoResourceSP resource
QSharedPointer< QByteArray > data