File indexing completed on 2024-05-12 15:59:48

0001 /*
0002  * SPDX-FileCopyrightText: 2018 Boudewijn Rempt <boud@valdyas.org>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "KisMemoryStorage.h"
0008 
0009 #include <QVector>
0010 #include <QFileInfo>
0011 
0012 #include <KisMimeDatabase.h>
0013 #include <kis_debug.h>
0014 #include <KisTag.h>
0015 #include <KisResourceStorage.h>
0016 #include <QBuffer>
0017 #include <KisGlobalResourcesInterface.h>
0018 #include <kis_pointer_utils.h>
0019 #include <KoMD5Generator.h>
0020 #include <kis_assert.h>
0021 
0022 
0023 struct StoredResource
0024 {
0025     QDateTime timestamp;
0026     QSharedPointer<QByteArray> data;
0027     KoResourceSP resource;
0028 };
0029 
0030 class MemoryTagIterator : public KisResourceStorage::TagIterator
0031 {
0032 public:
0033 
0034     MemoryTagIterator(QVector<KisTagSP> /*tags*/, const QString &resourceType)
0035         : m_resourceType(resourceType)
0036     {
0037     }
0038 
0039     bool hasNext() const override
0040     {
0041         return false;
0042     }
0043 
0044     void next() override
0045     {
0046     }
0047 
0048     KisTagSP tag() const override
0049     {
0050         return nullptr;
0051     }
0052 
0053 private:
0054     QString m_resourceType;
0055 };
0056 
0057 
0058 class MemoryItem : public KisResourceStorage::ResourceItem
0059 {
0060 public:
0061     ~MemoryItem() override {}
0062 };
0063 
0064 
0065 class KisMemoryStorage::Private {
0066 public:
0067     QHash<QString, QHash<QString, StoredResource>> resourcesNew;
0068     QHash<QString, QVector<KisTagSP>> tags;
0069     QMap<QString, QVariant> metadata;
0070 };
0071 
0072 
0073 KisMemoryStorage::KisMemoryStorage(const QString &location)
0074     : KisStoragePlugin(location)
0075     , d(new Private)
0076 {
0077 }
0078 
0079 KisMemoryStorage::~KisMemoryStorage()
0080 {
0081 }
0082 
0083 KisMemoryStorage::KisMemoryStorage(const KisMemoryStorage &rhs)
0084     : KisStoragePlugin(rhs.location())
0085     , d(new Private)
0086 {
0087     *this = rhs;
0088     d->resourcesNew = rhs.d->resourcesNew;
0089     d->tags = rhs.d->tags;
0090     d->metadata = rhs.d->metadata;
0091 }
0092 
0093 KisMemoryStorage &KisMemoryStorage::operator=(const KisMemoryStorage &rhs)
0094 {
0095     if (this != &rhs) {
0096         d->resourcesNew = rhs.d->resourcesNew;
0097 
0098         Q_FOREACH(const QString &key, rhs.d->tags.keys()) {
0099             Q_FOREACH(const KisTagSP tag, rhs.d->tags[key]) {
0100                 if (!d->tags.contains(key)) {
0101                     d->tags[key] = QVector<KisTagSP>();
0102                 }
0103                 d->tags[key] << tag->clone();
0104             }
0105         }
0106     }
0107     return *this;
0108 }
0109 
0110 bool KisMemoryStorage::saveAsNewVersion(const QString &resourceType, KoResourceSP resource)
0111 {
0112     QHash<QString, StoredResource> &typedResources =
0113         d->resourcesNew[resourceType];
0114 
0115     auto checkExists =
0116         [&typedResources] (const QString &filename) {
0117             return typedResources.contains(filename);
0118         };
0119 
0120     const QString newFilename =
0121         KisStorageVersioningHelper::chooseUniqueName(resource, 0, checkExists);
0122 
0123     if (newFilename.isEmpty()) return false;
0124 
0125     resource->setFilename(newFilename);
0126 
0127     StoredResource storedResource;
0128     storedResource.timestamp = QDateTime::currentDateTime();
0129     storedResource.data.reset(new QByteArray());
0130     QBuffer buffer(storedResource.data.data());
0131     buffer.open(QIODevice::WriteOnly);
0132     bool result = resource->saveToDevice(&buffer);
0133     buffer.close();
0134     if (!result) {
0135         storedResource.resource = resource;
0136     }
0137 
0138     typedResources.insert(newFilename, storedResource);
0139 
0140     return true;
0141 }
0142 
0143 KisResourceStorage::ResourceItem KisMemoryStorage::resourceItem(const QString &url)
0144 {
0145     MemoryItem item;
0146     item.url = url;
0147     item.folder = QString();
0148     item.lastModified = QDateTime::fromMSecsSinceEpoch(0);
0149     return item;
0150 }
0151 
0152 bool KisMemoryStorage::loadVersionedResource(KoResourceSP resource)
0153 {
0154     const QString resourceType = resource->resourceType().first;
0155     const QString resourceFilename = resource->filename();
0156 
0157     bool retval = false;
0158 
0159     if (d->resourcesNew.contains(resourceType) &&
0160         d->resourcesNew[resourceType].contains(resourceFilename)) {
0161 
0162         const StoredResource &storedResource =
0163             d->resourcesNew[resourceType][resourceFilename];
0164 
0165         if (storedResource.data->size() > 0) {
0166             QBuffer buffer(storedResource.data.data());
0167             buffer.open(QIODevice::ReadOnly);
0168             resource->loadFromDevice(&buffer, KisGlobalResourcesInterface::instance());
0169         } else {
0170             KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(storedResource.data->size() > 0, false);
0171             qWarning() << "Cannot load resource from device in KisMemoryStorage::loadVersionedResource";
0172             return false;
0173         }
0174         retval = true;
0175     }
0176 
0177     return retval;
0178 }
0179 
0180 bool KisMemoryStorage::importResource(const QString &url, QIODevice *device)
0181 {
0182     QStringList parts = url.split('/', QString::SkipEmptyParts);
0183     Q_ASSERT(parts.size() == 2);
0184 
0185     const QString resourceType = parts[0];
0186     const QString resourceFilename = parts[1];
0187 
0188     // we cannot overwrite exising file by API convention
0189     if (d->resourcesNew.contains(resourceType) &&
0190         d->resourcesNew[resourceType].contains(resourceFilename)) {
0191         return false;
0192     }
0193 
0194     StoredResource storedResource;
0195     storedResource.timestamp = QDateTime::currentDateTime();
0196     storedResource.data.reset(new QByteArray(device->readAll()));
0197 
0198     QHash<QString, StoredResource> &typedResources =
0199         d->resourcesNew[resourceType];
0200     typedResources.insert(resourceFilename, storedResource);
0201 
0202     return true;
0203 }
0204 
0205 bool KisMemoryStorage::exportResource(const QString &url, QIODevice *device)
0206 {
0207     QStringList parts = url.split('/', QString::SkipEmptyParts);
0208     Q_ASSERT(parts.size() == 2);
0209 
0210     const QString resourceType = parts[0];
0211     const QString resourceFilename = parts[1];
0212 
0213     if (!d->resourcesNew.contains(resourceType) ||
0214         !d->resourcesNew[resourceType].contains(resourceFilename)) {
0215         return false;
0216     }
0217 
0218     const StoredResource &storedResource =
0219         d->resourcesNew[resourceType][resourceFilename];
0220 
0221     if (!storedResource.data) {
0222         qWarning() << "Stored resource doesn't have a seriallized representation!";
0223         return false;
0224     }
0225 
0226     device->write(*storedResource.data);
0227     return true;
0228 }
0229 
0230 bool KisMemoryStorage::addResource(const QString &resourceType,  KoResourceSP resource)
0231 {
0232     QHash<QString, StoredResource> &typedResources = d->resourcesNew[resourceType];
0233 
0234     if (typedResources.contains(resource->filename())) {
0235         return true;
0236     };
0237 
0238     StoredResource storedResource;
0239     storedResource.timestamp = QDateTime::currentDateTime();
0240     storedResource.data.reset(new QByteArray());
0241     if (resource->isSerializable()) {
0242         QBuffer buffer(storedResource.data.data());
0243         buffer.open(QIODevice::WriteOnly);
0244         if (!resource->saveToDevice(&buffer)) {
0245             storedResource.resource = resource;
0246         }
0247         buffer.close();
0248     } else {
0249         storedResource.resource = resource;
0250     }
0251 
0252     typedResources.insert(resource->filename(), storedResource);
0253 
0254     return true;
0255 }
0256 
0257 QString KisMemoryStorage::resourceMd5(const QString &url)
0258 {
0259     QStringList parts = url.split('/', QString::SkipEmptyParts);
0260     Q_ASSERT(parts.size() == 2);
0261 
0262     const QString resourceType = parts[0];
0263     const QString resourceFilename = parts[1];
0264 
0265     QString result;
0266 
0267     if (d->resourcesNew.contains(resourceType) &&
0268         d->resourcesNew[resourceType].contains(resourceFilename)) {
0269 
0270         const StoredResource &storedResource =
0271             d->resourcesNew[resourceType][resourceFilename];
0272 
0273         if (storedResource.data->size() > 0 || storedResource.resource.isNull()) {
0274             result = KoMD5Generator::generateHash(*storedResource.data);
0275         } else {
0276             result = storedResource.resource->md5Sum();
0277         }
0278     }
0279 
0280     return result;
0281 }
0282 
0283 QSharedPointer<KisResourceStorage::ResourceIterator> KisMemoryStorage::resources(const QString &resourceType)
0284 {
0285     QVector<VersionedResourceEntry> entries;
0286 
0287 
0288     QHash<QString, StoredResource> &typedResources =
0289         d->resourcesNew[resourceType];
0290 
0291     for (auto it = typedResources.begin(); it != typedResources.end(); ++it) {
0292         VersionedResourceEntry entry;
0293         entry.filename = it.key();
0294         entry.lastModified = it.value().timestamp;
0295         entry.tagList = {}; // TODO
0296         entry.resourceType = resourceType;
0297         entries.append(entry);
0298 
0299     }
0300 
0301     KisStorageVersioningHelper::detectFileVersions(entries);
0302 
0303     return toQShared(new KisVersionedStorageIterator(entries, this));
0304 }
0305 
0306 QSharedPointer<KisResourceStorage::TagIterator> KisMemoryStorage::tags(const QString &resourceType)
0307 {
0308     return QSharedPointer<KisResourceStorage::TagIterator>(new MemoryTagIterator(d->tags[resourceType], resourceType));
0309 }
0310 
0311 void KisMemoryStorage::setMetaData(const QString &key, const QVariant &value)
0312 {
0313     d->metadata[key] = value;
0314 }
0315 
0316 QStringList KisMemoryStorage::metaDataKeys() const
0317 {
0318     return QStringList() << KisResourceStorage::s_meta_name;
0319 }
0320 
0321 QVariant KisMemoryStorage::metaData(const QString &key) const
0322 {
0323     QVariant r;
0324     if (d->metadata.contains(key)) {
0325         r = d->metadata[key];
0326     }
0327     return r;
0328 }