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

0001 /*
0002  * SPDX-FileCopyrightText: 2018 Boudewijn Rempt <boud@valdyas.org>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #ifndef KISRESOURCESTORAGE_H
0008 #define KISRESOURCESTORAGE_H
0009 
0010 #include <QSharedPointer>
0011 #include <QScopedPointer>
0012 #include <QString>
0013 #include <QDateTime>
0014 #include <QMap>
0015 
0016 #include <KoResource.h>
0017 #include <KisTag.h>
0018 
0019 #include <klocalizedstring.h>
0020 
0021 #include <kritaresources_export.h>
0022 
0023 class KisStoragePlugin;
0024 
0025 class KisStoragePluginFactoryBase
0026 {
0027 public:
0028     virtual ~KisStoragePluginFactoryBase(){}
0029     virtual KisStoragePlugin *create(const QString &/*location*/) { return 0; }
0030 };
0031 
0032 template<typename T>
0033 class KisStoragePluginFactory : public KisStoragePluginFactoryBase
0034 {
0035 public:
0036     KisStoragePlugin *create(const QString &location) override {
0037         return new T(location);
0038     }
0039 };
0040 
0041 class KisResourceStorage;
0042 typedef QSharedPointer<KisResourceStorage> KisResourceStorageSP;
0043 
0044 
0045 /**
0046  * The KisResourceStorage class is the base class for
0047  * places where resources can be stored. Examples are
0048  * folders, bundles or Adobe resource libraries like
0049  * ABR files.
0050  */
0051 class KRITARESOURCES_EXPORT KisResourceStorage
0052 {
0053 public:
0054 
0055     /// A resource item is simply an entry in the storage,
0056     struct ResourceItem {
0057 
0058         virtual ~ResourceItem() {}
0059         QString url;
0060         QString folder;
0061         QString resourceType;
0062         QDateTime lastModified;
0063     };
0064 
0065     class KRITARESOURCES_EXPORT TagIterator
0066     {
0067     public:
0068         virtual ~TagIterator() {}
0069         virtual bool hasNext() const = 0;
0070         /// The iterator is only valid if next() has been called at least once.
0071         virtual void next() = 0;
0072 
0073         /// A tag object on which we can set properties and which we can save
0074         virtual KisTagSP tag() const = 0;
0075     };
0076 
0077     class KRITARESOURCES_EXPORT ResourceIterator
0078     {
0079     public:
0080 
0081         virtual ~ResourceIterator() {}
0082 
0083         virtual bool hasNext() const = 0;
0084         /// The iterator is only valid if next() has been called at least once.
0085         virtual void next() = 0;
0086 
0087         virtual QString url() const = 0;
0088         virtual QString type() const = 0;
0089         virtual QDateTime lastModified() const = 0;
0090         virtual int guessedVersion() const { return 0; }
0091         virtual QSharedPointer<KisResourceStorage::ResourceIterator> versions() const;
0092 
0093         KoResourceSP resource() const;
0094 
0095     protected:
0096         /// This only loads the resource when called
0097         virtual KoResourceSP resourceImpl() const = 0;
0098 
0099     private:
0100         mutable KoResourceSP m_cachedResource;
0101         mutable QString m_cachedResourceUrl;
0102     };
0103 
0104     enum class StorageType : int {
0105         Unknown = 1,
0106         Folder = 2,
0107         Bundle = 3,
0108         AdobeBrushLibrary = 4,
0109         AdobeStyleLibrary = 5,
0110         Memory = 6
0111     };
0112 
0113     static QString storageTypeToString(StorageType storageType) {
0114         switch (storageType) {
0115         case StorageType::Unknown:
0116             return i18n("Unknown");
0117         case StorageType::Folder:
0118             return i18n("Folder");
0119         case StorageType::Bundle:
0120             return i18n("Bundle");
0121         case StorageType::AdobeBrushLibrary:
0122             return i18n("Adobe Brush Library");
0123         case StorageType::AdobeStyleLibrary:
0124             return i18n("Adobe Style Library");
0125         case StorageType::Memory:
0126             return i18n("Memory");
0127         default:
0128             return i18n("Invalid");
0129         }
0130     }
0131 
0132 
0133     static QString storageTypeToUntranslatedString(StorageType storageType) {
0134         switch (storageType) {
0135         case StorageType::Unknown:
0136             return ("Unknown");
0137         case StorageType::Folder:
0138             return ("Folder");
0139         case StorageType::Bundle:
0140             return ("Bundle");
0141         case StorageType::AdobeBrushLibrary:
0142             return ("Adobe Brush Library");
0143         case StorageType::AdobeStyleLibrary:
0144             return ("Adobe Style Library");
0145         case StorageType::Memory:
0146             return ("Memory");
0147         default:
0148             return ("Invalid");
0149         }
0150     }
0151 
0152 
0153     KisResourceStorage(const QString &location);
0154     ~KisResourceStorage();
0155     KisResourceStorage(const KisResourceStorage &rhs);
0156     KisResourceStorage &operator=(const KisResourceStorage &rhs);
0157     KisResourceStorageSP clone() const;
0158 
0159     /// The filename of the storage if it's a bundle or Adobe Library. This can
0160     /// also be empty (for the folder storage) or "memory" for the storage for
0161     /// temporary resources, a UUID for storages associated with documents.
0162     QString name() const;
0163 
0164     /// The absolute location of the storage
0165     QString location() const;
0166 
0167     /// true if the storage exists and can be used
0168     bool valid() const;
0169 
0170     /// The type of the storage
0171     StorageType type() const;
0172 
0173     /// The icond for the storage
0174     QImage thumbnail() const;
0175 
0176     /// The time and date when the storage was last modified, or created
0177     /// for memory storages.
0178     QDateTime timestamp() const;
0179 
0180     /// The time and date when the resource was last modified
0181     /// For filestorage
0182     QDateTime timeStampForResource(const QString &resourceType, const QString &filename) const;
0183 
0184     /// And entry in the storage; this is not the loaded resource
0185     ResourceItem resourceItem(const QString &url);
0186 
0187     /// The loaded resource for an entry in the storage
0188     KoResourceSP resource(const QString &url);
0189 
0190     /// The MD5 checksum of the resource in the storage
0191     QString resourceMd5(const QString &url);
0192 
0193     /// If the resource is present on the filesystem as a distinct fine,
0194     /// returns the full file path of it, otherwise returns an empty string.
0195     ///
0196     /// Never manipulate the file in any way directly! It will destroy the
0197     /// resources database. Use this file path only for informational purposes.
0198     QString resourceFilePath(const QString &url);
0199 
0200     /// An iterator over all the resources in the storage
0201     QSharedPointer<ResourceIterator> resources(const QString &resourceType) const;
0202 
0203     /// An iterator over all the tags in the resource
0204     QSharedPointer<TagIterator> tags(const QString &resourceType) const;
0205 
0206     /// Adds a tag to the storage, however, it does not store the links between
0207     /// tags and resources.
0208     bool addTag(const QString &resourceType, KisTagSP tag);
0209 
0210     /// Creates a new version of the given resource.
0211     bool saveAsNewVersion(KoResourceSP resource);
0212 
0213     /// Adds the given resource to the storage. If there is already a resource
0214     /// with the given filename of the given type, this should return false and
0215     /// saveAsnewVersion should be used.
0216     bool addResource(KoResourceSP resource);
0217 
0218     /**
0219      * Copies the given file into this storage. Implementations should not overwrite
0220      * an existing resource with the same filename, but return false.
0221      *
0222      * @param url is the URL of the resource inside the storage, which is usually
0223      *            resource_type/resource_filename.ext
0224      */
0225     bool importResource(const QString &url, QIODevice *device);
0226 
0227     /**
0228      * Copies the given given resource from the storage into \p device
0229      *
0230      * @param url is the URL of the resource inside the storage, which is usually
0231      *            resource_type/resource_filename.ext
0232      */
0233     bool exportResource(const QString &url, QIODevice *device);
0234 
0235     /// Returns true if the storage supports versioning of the resources.
0236     /// It enables loadVersionedResource() call.
0237     bool supportsVersioning() const;
0238 
0239     /// Reloads the given resource from the persistent storage
0240     bool loadVersionedResource(KoResourceSP resource);
0241 
0242     static const QString s_meta_generator;
0243     static const QString s_meta_author;
0244     static const QString s_meta_title;
0245     static const QString s_meta_description;
0246     static const QString s_meta_initial_creator;
0247     static const QString s_meta_creator;
0248     static const QString s_meta_creation_date;
0249     static const QString s_meta_dc_date;
0250     static const QString s_meta_user_defined;
0251     static const QString s_meta_name;
0252     static const QString s_meta_value;
0253     static const QString s_meta_version;
0254     static const QString s_meta_license;
0255     static const QString s_meta_email;
0256     static const QString s_meta_website;
0257 
0258     void setMetaData(const QString &key, const QVariant &value);
0259     QStringList metaDataKeys() const;
0260     QVariant metaData(const QString &key) const;
0261 
0262 private:
0263 
0264     friend class KisStorageModel;
0265     friend class KisResourceLocator;
0266     friend class KisResourceCacheDb;
0267 
0268     void setStorageId(int storageId);
0269     int storageId();
0270 
0271     class Private;
0272     QScopedPointer<Private> d;
0273 };
0274 
0275 
0276 
0277 inline QDebug operator<<(QDebug dbg, const KisResourceStorageSP storage)
0278 {
0279     if (storage.isNull()) {
0280         dbg.nospace() << "[RESOURCESTORAGE] NULL";
0281     }
0282     else {
0283         dbg.nospace() << "[RESOURCESTORAGE] Name: " << storage->name()
0284                       << " Version: " << storage->location()
0285                       << " Valid: " << storage->valid()
0286                       << " Storage: " << KisResourceStorage::storageTypeToString(storage->type())
0287                       << " Timestamp: " << storage->timestamp()
0288                       << " Pointer: " << storage.data();
0289     }
0290     return dbg.space();
0291 }
0292 
0293 class KRITARESOURCES_EXPORT KisStoragePluginRegistry {
0294 public:
0295     KisStoragePluginRegistry();
0296     virtual ~KisStoragePluginRegistry();
0297 
0298     void addStoragePluginFactory(KisResourceStorage::StorageType storageType, KisStoragePluginFactoryBase *factory);
0299     static KisStoragePluginRegistry *instance();
0300 private:
0301     friend class KisResourceStorage;
0302     QMap<KisResourceStorage::StorageType, KisStoragePluginFactoryBase*> m_storageFactoryMap;
0303 
0304 };
0305 
0306 struct VersionedResourceEntry
0307 {
0308     QString resourceType;
0309     QString filename;
0310     QList<QString> tagList;
0311     QDateTime lastModified;
0312     int guessedVersion = -1;
0313     QString guessedKey;
0314 
0315     struct KeyVersionLess {
0316         bool operator()(const VersionedResourceEntry &lhs, const VersionedResourceEntry &rhs) const {
0317             return lhs.guessedKey < rhs.guessedKey ||
0318                 (lhs.guessedKey == rhs.guessedKey && lhs.guessedVersion < rhs.guessedVersion);
0319         }
0320     };
0321 
0322     struct KeyLess {
0323         bool operator()(const VersionedResourceEntry &lhs, const VersionedResourceEntry &rhs) const {
0324             return lhs.guessedKey < rhs.guessedKey;
0325         }
0326     };
0327 };
0328 
0329 class KRITARESOURCES_EXPORT KisStorageVersioningHelper {
0330 public:
0331 
0332     static bool addVersionedResource(const QString &saveLocation, KoResourceSP resource, int minVersion);
0333     static QString chooseUniqueName(KoResourceSP resource,
0334                                     int minVersion,
0335                                     std::function<bool(QString)> checkExists);
0336 
0337     static void detectFileVersions(QVector<VersionedResourceEntry> &allFiles);
0338 
0339 
0340 };
0341 
0342 class KisVersionedStorageIterator : public KisResourceStorage::ResourceIterator
0343 {
0344 public:
0345     KisVersionedStorageIterator(const QVector<VersionedResourceEntry> &entries,
0346                                 KisStoragePlugin *_q);
0347 
0348     bool hasNext() const override;
0349     void next() override;
0350     QString url() const override;
0351     QString type() const override;
0352     QDateTime lastModified() const override;
0353     KoResourceSP resourceImpl() const override;
0354 
0355     int guessedVersion() const override;
0356 
0357     QSharedPointer<KisResourceStorage::ResourceIterator> versions() const override;
0358 
0359 protected:
0360     KisVersionedStorageIterator(const QVector<VersionedResourceEntry> &entries,
0361                                 QVector<VersionedResourceEntry>::const_iterator begin,
0362                                 QVector<VersionedResourceEntry>::const_iterator end,
0363                                 KisStoragePlugin *_q);
0364 protected:
0365     KisStoragePlugin *q = 0;
0366     const QVector<VersionedResourceEntry> m_entries;
0367     QVector<VersionedResourceEntry>::const_iterator m_it;
0368     QVector<VersionedResourceEntry>::const_iterator m_chunkStart;
0369     QVector<VersionedResourceEntry>::const_iterator m_begin;
0370     QVector<VersionedResourceEntry>::const_iterator m_end;
0371     bool m_isStarted = false;
0372 };
0373 
0374 
0375 #endif // KISRESOURCESTORAGE_H