File indexing completed on 2024-06-16 04:50:16
0001 /* 0002 SPDX-FileCopyrightText: 2008 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #pragma once 0008 0009 #include "cachepolicy.h" 0010 #include "collection.h" 0011 #include "collectionfetchscope.h" 0012 #include "collectionutils.h" 0013 #include "item.h" 0014 #include "itemfetchscope.h" 0015 #include "sharedvaluepool_p.h" 0016 #include "tag.h" 0017 0018 #include "private/imapparser_p.h" 0019 #include "private/protocol_p.h" 0020 #include "private/scope_p.h" 0021 #include "private/tristate_p.h" 0022 0023 #include <QString> 0024 0025 #include <algorithm> 0026 #include <cassert> 0027 #include <functional> 0028 #include <set> 0029 #include <type_traits> 0030 0031 namespace Akonadi 0032 { 0033 struct ProtocolHelperValuePool { 0034 using FlagPool = Internal::SharedValuePool<QByteArray, QList>; 0035 using MimeTypePool = Internal::SharedValuePool<QString, QList>; 0036 0037 FlagPool flagPool; 0038 MimeTypePool mimeTypePool; 0039 QHash<Collection::Id, Collection> ancestorCollections; 0040 }; 0041 0042 /** 0043 @internal 0044 Helper methods for converting between libakonadi objects and their protocol 0045 representation. 0046 0047 @todo Add unit tests for this. 0048 @todo Use exceptions for a useful error handling 0049 */ 0050 class ProtocolHelper 0051 { 0052 public: 0053 /** Part namespaces. */ 0054 enum PartNamespace { 0055 PartGlobal, 0056 PartPayload, 0057 PartAttribute, 0058 }; 0059 0060 /** 0061 Parse a cache policy definition. 0062 @param policy The parsed cache policy. 0063 @returns Akonadi::CachePolicy 0064 */ 0065 static CachePolicy parseCachePolicy(const Protocol::CachePolicy &policy); 0066 0067 /** 0068 Convert a cache policy object into its protocol representation. 0069 */ 0070 static Protocol::CachePolicy cachePolicyToProtocol(const CachePolicy &policy); 0071 0072 /** 0073 Convert a ancestor chain from its protocol representation into an Item object. 0074 */ 0075 static void parseAncestors(const QList<Protocol::Ancestor> &ancestors, Item *item); 0076 0077 /** 0078 Convert a ancestor chain from its protocol representation into a Collection object. 0079 */ 0080 static void parseAncestors(const QList<Protocol::Ancestor> &ancestors, Collection *collection); 0081 0082 /** 0083 Convert a ancestor chain from its protocol representation into an Item object. 0084 0085 This method allows to pass a @p valuePool which acts as cache, so ancestor paths for the 0086 same @p parentCollection don't have to be parsed twice. 0087 */ 0088 static void 0089 parseAncestorsCached(const QList<Protocol::Ancestor> &ancestors, Item *item, Collection::Id parentCollection, ProtocolHelperValuePool *valuePool = nullptr); 0090 0091 /** 0092 Convert a ancestor chain from its protocol representation into an Collection object. 0093 0094 This method allows to pass a @p valuePool which acts as cache, so ancestor paths for the 0095 same @p parentCollection don't have to be parsed twice. 0096 */ 0097 static void parseAncestorsCached(const QList<Protocol::Ancestor> &ancestors, 0098 Collection *collection, 0099 Collection::Id parentCollection, 0100 ProtocolHelperValuePool *valuePool = nullptr); 0101 /** 0102 Parse a collection description. 0103 @param data The input data. 0104 @param requireParent Whether or not we require a parent as part of the data. 0105 @returns The parsed collection 0106 */ 0107 static Collection parseCollection(const Protocol::FetchCollectionsResponse &data, bool requireParent = true); 0108 0109 static Tag parseTag(const Protocol::FetchTagsResponse &data); 0110 0111 static void parseAttributes(const Protocol::Attributes &attributes, Item *item); 0112 static void parseAttributes(const Protocol::Attributes &attributes, Collection *collection); 0113 static void parseAttributes(const Protocol::Attributes &attributes, Tag *entity); 0114 0115 static CollectionStatistics parseCollectionStatistics(const Protocol::FetchCollectionStatsResponse &stats); 0116 0117 /** 0118 Convert attributes to their protocol representation. 0119 */ 0120 static Protocol::Attributes attributesToProtocol(const Item &item, bool ns = false); 0121 static Protocol::Attributes attributesToProtocol(const Collection &collection, bool ns = false); 0122 static Protocol::Attributes attributesToProtocol(const Tag &entity, bool ns = false); 0123 static Protocol::Attributes attributesToProtocol(const std::vector<Attribute *> &modifiedAttributes, bool ns = false); 0124 0125 /** 0126 Encodes part label and namespace. 0127 */ 0128 static QByteArray encodePartIdentifier(PartNamespace ns, const QByteArray &label); 0129 0130 /** 0131 Decode part label and namespace. 0132 */ 0133 static QByteArray decodePartIdentifier(const QByteArray &data, PartNamespace &ns); 0134 0135 /** 0136 Converts the given set of items into a protocol representation. 0137 @throws A Akonadi::Exception if the item set contains items with missing/invalid identifiers. 0138 */ 0139 template<typename T, template<typename> class Container> 0140 static Scope entitySetToScope(const Container<T> &_objects) 0141 { 0142 if (_objects.isEmpty()) { 0143 throw Exception("No objects specified"); 0144 } 0145 0146 Container<T> objects(_objects); 0147 using namespace std::placeholders; 0148 std::sort(objects.begin(), objects.end(), [](const T &a, const T &b) -> bool { 0149 return a.id() < b.id(); 0150 }); 0151 if (objects.at(0).isValid()) { 0152 QList<typename T::Id> uids; 0153 uids.reserve(objects.size()); 0154 for (const T &object : objects) { 0155 uids << object.id(); 0156 } 0157 ImapSet set; 0158 set.add(uids); 0159 return Scope(set); 0160 } 0161 0162 if (entitySetHasGID(_objects)) { 0163 return entitySetToGID(_objects); 0164 } 0165 0166 if (!entitySetHasRemoteIdentifier(_objects, std::mem_fn(&T::remoteId))) { 0167 throw Exception("No remote identifier specified"); 0168 } 0169 0170 // check if we have RIDs or HRIDs 0171 if (entitySetHasHRID(_objects)) { 0172 return hierarchicalRidToScope(objects.first()); 0173 } 0174 0175 return entitySetToRemoteIdentifier(Scope::Rid, _objects, std::mem_fn(&T::remoteId)); 0176 } 0177 0178 static Protocol::ScopeContext commandContextToProtocol(const Akonadi::Collection &collection, const Akonadi::Tag &tag, const Item::List &requestedItems); 0179 0180 /** 0181 Converts the given object identifier into a protocol representation. 0182 @throws A Akonadi::Exception if the item set contains items with missing/invalid identifiers. 0183 */ 0184 template<typename T> 0185 static Scope entityToScope(const T &object) 0186 { 0187 return entitySetToScope(QList<T>() << object); 0188 } 0189 0190 /** 0191 Converts the given collection's hierarchical RID into a protocol representation. 0192 Assumes @p col has a valid hierarchical RID, so check that before! 0193 */ 0194 static Scope hierarchicalRidToScope(const Collection &col); 0195 0196 /** 0197 Converts the HRID of the given item into an ASAP protocol representation. 0198 Assumes @p item has a valid HRID. 0199 */ 0200 static Scope hierarchicalRidToScope(const Item &item); 0201 0202 static Scope hierarchicalRidToScope(const Tag & /*tag*/) 0203 { 0204 assert(false); 0205 return Scope(); 0206 } 0207 0208 /** 0209 Converts a given ItemFetchScope object into a protocol representation. 0210 */ 0211 static Protocol::ItemFetchScope itemFetchScopeToProtocol(const ItemFetchScope &fetchScope); 0212 static ItemFetchScope parseItemFetchScope(const Protocol::ItemFetchScope &fetchScope); 0213 0214 static Protocol::CollectionFetchScope collectionFetchScopeToProtocol(const CollectionFetchScope &fetchScope); 0215 static CollectionFetchScope parseCollectionFetchScope(const Protocol::CollectionFetchScope &fetchScope); 0216 0217 static Protocol::TagFetchScope tagFetchScopeToProtocol(const TagFetchScope &fetchScope); 0218 static TagFetchScope parseTagFetchScope(const Protocol::TagFetchScope &fetchScope); 0219 0220 /** 0221 * Parses a single line from an item fetch job result into an Item object. 0222 * FIXME: std::optional 0223 */ 0224 static Item 0225 parseItemFetchResult(const Protocol::FetchItemsResponse &data, const ItemFetchScope *fetchScope = nullptr, ProtocolHelperValuePool *valuePool = nullptr); 0226 static Tag parseTagFetchResult(const Protocol::FetchTagsResponse &data); 0227 static Relation parseRelationFetchResult(const Protocol::FetchRelationsResponse &data); 0228 0229 static bool streamPayloadToFile(const QString &file, const QByteArray &data, QByteArray &error); 0230 0231 static Akonadi::Tristate listPreference(const Collection::ListPreference pref); 0232 0233 private: 0234 template<typename T, template<typename> class Container> 0235 inline static bool entitySetHasGID(const Container<T> &objects) 0236 { 0237 if constexpr (std::is_same_v<T, Akonadi::Collection>) { 0238 Q_UNUSED(objects); 0239 return false; 0240 } else { 0241 return entitySetHasRemoteIdentifier(objects, std::mem_fn(&T::gid)); 0242 } 0243 } 0244 0245 template<typename T, template<typename> class Container> 0246 inline static Scope entitySetToGID(const Container<T> &objects) 0247 { 0248 if constexpr (std::is_same_v<T, Akonadi::Collection>) { 0249 Q_UNUSED(objects); 0250 return Scope(); 0251 } else { 0252 return entitySetToRemoteIdentifier(Scope::Gid, objects, std::mem_fn(&T::gid)); 0253 } 0254 } 0255 0256 template<typename T, template<typename> class Container, typename RIDFunc> 0257 inline static bool entitySetHasRemoteIdentifier(const Container<T> &objects, const RIDFunc &ridFunc) 0258 { 0259 return std::find_if(objects.constBegin(), 0260 objects.constEnd(), 0261 [=](const T &obj) { 0262 return ridFunc(obj).isEmpty(); 0263 }) 0264 == objects.constEnd(); 0265 } 0266 0267 template<typename T, template<typename> class Container, typename RIDFunc> 0268 inline static Scope entitySetToRemoteIdentifier(Scope::SelectionScope scope, const Container<T> &objects, RIDFunc &&ridFunc) 0269 { 0270 QStringList rids; 0271 rids.reserve(objects.size()); 0272 std::transform(objects.cbegin(), objects.cend(), std::back_inserter(rids), [=](const T &obj) -> QString { 0273 if constexpr (std::is_same_v<QString, std::remove_cvref_t<std::invoke_result_t<RIDFunc, const T &>>>) { 0274 return ridFunc(obj); 0275 } else { 0276 return QString::fromLatin1(ridFunc(obj)); 0277 } 0278 }); 0279 return Scope(scope, rids); 0280 } 0281 0282 template<typename T, template<typename> class Container> 0283 inline static bool entitySetHasHRID(const Container<T> &objects) 0284 { 0285 if constexpr (std::is_same_v<T, Tag>) { 0286 return false; 0287 } else { 0288 return objects.size() == 1 0289 && std::find_if(objects.constBegin(), 0290 objects.constEnd(), 0291 [](const T &obj) -> bool { 0292 return !CollectionUtils::hasValidHierarchicalRID(obj); 0293 }) 0294 == objects.constEnd(); // ### HRID sets are not yet specified 0295 } 0296 } 0297 }; 0298 0299 }