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 }