File indexing completed on 2024-05-19 04:27:36
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 0183 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) 0184 QStringList parts = url.split('/', Qt::SkipEmptyParts); 0185 #else 0186 QStringList parts = url.split('/', QString::SkipEmptyParts); 0187 #endif 0188 0189 Q_ASSERT(parts.size() == 2); 0190 0191 const QString resourceType = parts[0]; 0192 const QString resourceFilename = parts[1]; 0193 0194 // we cannot overwrite existing file by API convention 0195 if (d->resourcesNew.contains(resourceType) && 0196 d->resourcesNew[resourceType].contains(resourceFilename)) { 0197 return false; 0198 } 0199 0200 StoredResource storedResource; 0201 storedResource.timestamp = QDateTime::currentDateTime(); 0202 storedResource.data.reset(new QByteArray(device->readAll())); 0203 0204 QHash<QString, StoredResource> &typedResources = 0205 d->resourcesNew[resourceType]; 0206 typedResources.insert(resourceFilename, storedResource); 0207 0208 return true; 0209 } 0210 0211 bool KisMemoryStorage::exportResource(const QString &url, QIODevice *device) 0212 { 0213 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) 0214 QStringList parts = url.split('/', Qt::SkipEmptyParts); 0215 #else 0216 QStringList parts = url.split('/', QString::SkipEmptyParts); 0217 #endif 0218 Q_ASSERT(parts.size() == 2); 0219 0220 const QString resourceType = parts[0]; 0221 const QString resourceFilename = parts[1]; 0222 0223 if (!d->resourcesNew.contains(resourceType) || 0224 !d->resourcesNew[resourceType].contains(resourceFilename)) { 0225 return false; 0226 } 0227 0228 const StoredResource &storedResource = 0229 d->resourcesNew[resourceType][resourceFilename]; 0230 0231 if (!storedResource.data) { 0232 qWarning() << "Stored resource doesn't have a serialized representation!"; 0233 return false; 0234 } 0235 0236 device->write(*storedResource.data); 0237 return true; 0238 } 0239 0240 bool KisMemoryStorage::addResource(const QString &resourceType, KoResourceSP resource) 0241 { 0242 QHash<QString, StoredResource> &typedResources = d->resourcesNew[resourceType]; 0243 0244 if (typedResources.contains(resource->filename())) { 0245 return true; 0246 }; 0247 0248 StoredResource storedResource; 0249 storedResource.timestamp = QDateTime::currentDateTime(); 0250 storedResource.data.reset(new QByteArray()); 0251 if (resource->isSerializable()) { 0252 QBuffer buffer(storedResource.data.data()); 0253 buffer.open(QIODevice::WriteOnly); 0254 if (!resource->saveToDevice(&buffer)) { 0255 storedResource.resource = resource; 0256 } 0257 buffer.close(); 0258 } else { 0259 storedResource.resource = resource; 0260 } 0261 0262 typedResources.insert(resource->filename(), storedResource); 0263 0264 return true; 0265 } 0266 0267 QString KisMemoryStorage::resourceMd5(const QString &url) 0268 { 0269 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) 0270 QStringList parts = url.split('/', Qt::SkipEmptyParts); 0271 #else 0272 QStringList parts = url.split('/', QString::SkipEmptyParts); 0273 #endif 0274 0275 Q_ASSERT(parts.size() == 2); 0276 0277 const QString resourceType = parts[0]; 0278 const QString resourceFilename = parts[1]; 0279 0280 QString result; 0281 0282 if (d->resourcesNew.contains(resourceType) && 0283 d->resourcesNew[resourceType].contains(resourceFilename)) { 0284 0285 const StoredResource &storedResource = 0286 d->resourcesNew[resourceType][resourceFilename]; 0287 0288 if (storedResource.data->size() > 0 || storedResource.resource.isNull()) { 0289 result = KoMD5Generator::generateHash(*storedResource.data); 0290 } else { 0291 result = storedResource.resource->md5Sum(); 0292 } 0293 } 0294 0295 return result; 0296 } 0297 0298 QSharedPointer<KisResourceStorage::ResourceIterator> KisMemoryStorage::resources(const QString &resourceType) 0299 { 0300 QVector<VersionedResourceEntry> entries; 0301 0302 0303 QHash<QString, StoredResource> &typedResources = 0304 d->resourcesNew[resourceType]; 0305 0306 for (auto it = typedResources.begin(); it != typedResources.end(); ++it) { 0307 VersionedResourceEntry entry; 0308 entry.filename = it.key(); 0309 entry.lastModified = it.value().timestamp; 0310 entry.tagList = {}; // TODO 0311 entry.resourceType = resourceType; 0312 entries.append(entry); 0313 0314 } 0315 0316 KisStorageVersioningHelper::detectFileVersions(entries); 0317 0318 return toQShared(new KisVersionedStorageIterator(entries, this)); 0319 } 0320 0321 QSharedPointer<KisResourceStorage::TagIterator> KisMemoryStorage::tags(const QString &resourceType) 0322 { 0323 return QSharedPointer<KisResourceStorage::TagIterator>(new MemoryTagIterator(d->tags[resourceType], resourceType)); 0324 } 0325 0326 void KisMemoryStorage::setMetaData(const QString &key, const QVariant &value) 0327 { 0328 d->metadata[key] = value; 0329 } 0330 0331 QStringList KisMemoryStorage::metaDataKeys() const 0332 { 0333 return QStringList() << KisResourceStorage::s_meta_name; 0334 } 0335 0336 QVariant KisMemoryStorage::metaData(const QString &key) const 0337 { 0338 QVariant r; 0339 if (d->metadata.contains(key)) { 0340 r = d->metadata[key]; 0341 } 0342 return r; 0343 }