protocolhelper_p.h 12 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
    Copyright (c) 2008 Volker Krause <vkrause@kde.org>

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

20 21
#ifndef AKONADI_PROTOCOLHELPER_P_H
#define AKONADI_PROTOCOLHELPER_P_H
22

Daniel Vrátil's avatar
Daniel Vrátil committed
23 24
#include "cachepolicy.h"
#include "collection.h"
25
#include "collectionutils.h"
Daniel Vrátil's avatar
Daniel Vrátil committed
26 27 28 29
#include "item.h"
#include "itemfetchscope.h"
#include "sharedvaluepool_p.h"
#include "tag.h"
30

31 32 33 34
#include "private/imapparser_p.h"
#include "private/protocol_p.h"
#include "private/scope_p.h"
#include "private/tristate_p.h"
35

36 37
#include <QString>

38
#include <algorithm>
39 40
#include <type_traits>
#include <functional>
41
#include <cassert>
42

Laurent Montel's avatar
Laurent Montel committed
43
namespace Akonadi
44
{
Laurent Montel's avatar
Laurent Montel committed
45 46

struct ProtocolHelperValuePool {
Guy Maurel's avatar
Guy Maurel committed
47 48
    typedef Internal::SharedValuePool<QByteArray, QVector> FlagPool;
    typedef Internal::SharedValuePool<QString, QVector> MimeTypePool;
49

Guy Maurel's avatar
Guy Maurel committed
50 51 52
    FlagPool flagPool;
    MimeTypePool mimeTypePool;
    QHash<Collection::Id, Collection> ancestorCollections;
53
};
54

55 56 57 58 59 60
/**
  @internal
  Helper methods for converting between libakonadi objects and their protocol
  representation.

  @todo Add unit tests for this.
61
  @todo Use exceptions for a useful error handling
62 63 64
*/
class ProtocolHelper
{
Guy Maurel's avatar
Guy Maurel committed
65
public:
66 67
    /** Part namespaces. */
    enum PartNamespace {
Guy Maurel's avatar
Guy Maurel committed
68 69 70
        PartGlobal,
        PartPayload,
        PartAttribute
71 72
    };

73 74
    /**
      Parse a cache policy definition.
75
      @param policy The parsed cache policy.
76
      @returns Akonadi::CachePolicy
77
    */
78
    static CachePolicy parseCachePolicy(const Protocol::CachePolicy &policy);
79 80 81 82

    /**
      Convert a cache policy object into its protocol representation.
    */
83
    static Protocol::CachePolicy cachePolicyToProtocol(const CachePolicy &policy);
84

85
    /**
86
      Convert a ancestor chain from its protocol representation into an Item object.
87
    */
88
    static void parseAncestors(const QVector<Protocol::Ancestor> &ancestors, Item *item);
89

90
    /**
91 92 93 94 95 96
      Convert a ancestor chain from its protocol representation into a Collection object.
    */
    static void parseAncestors(const QVector<Protocol::Ancestor> &ancestors, Collection *collection);

    /**
      Convert a ancestor chain from its protocol representation into an Item object.
97 98 99 100

      This method allows to pass a @p valuePool which acts as cache, so ancestor paths for the
      same @p parentCollection don't have to be parsed twice.
    */
101
    static void parseAncestorsCached(const QVector<Protocol::Ancestor> &ancestors,
102
                                     Item *item,
103
                                     Collection::Id parentCollection,
Laurent Montel's avatar
Laurent Montel committed
104
                                     ProtocolHelperValuePool *valuePool = nullptr);
105

106 107 108 109 110 111 112 113 114
    /**
      Convert a ancestor chain from its protocol representation into an Collection object.

      This method allows to pass a @p valuePool which acts as cache, so ancestor paths for the
      same @p parentCollection don't have to be parsed twice.
    */
    static void parseAncestorsCached(const QVector<Protocol::Ancestor> &ancestors,
                                     Collection *collection,
                                     Collection::Id parentCollection,
Laurent Montel's avatar
Laurent Montel committed
115
                                     ProtocolHelperValuePool *valuePool = nullptr);
116 117 118
    /**
      Parse a collection description.
      @param data The input data.
Yuri Chornoivan's avatar
Yuri Chornoivan committed
119
      @param requireParent Whether or not we require a parent as part of the data.
120
      @returns The parsed collection
121
    */
122 123
    static Collection parseCollection(const Protocol::FetchCollectionsResponse &data, bool requireParent = true);

124 125 126
    static void parseAttributes(const Protocol::Attributes &attributes, Item *item);
    static void parseAttributes(const Protocol::Attributes &attributes, Collection *collection);
    static void parseAttributes(const Protocol::Attributes &attributes, Tag *entity);
127 128

    static CollectionStatistics parseCollectionStatistics(const Protocol::FetchCollectionStatsResponse &stats);
129

130 131 132
    /**
      Convert attributes to their protocol representation.
    */
133 134 135
    static Protocol::Attributes attributesToProtocol(const Item &item, bool ns = false);
    static Protocol::Attributes attributesToProtocol(const Collection &collection, bool ns = false);
    static Protocol::Attributes attributesToProtocol(const Tag &entity, bool ns = false);
136 137 138 139

    /**
      Encodes part label and namespace.
    */
140
    static QByteArray encodePartIdentifier(PartNamespace ns, const QByteArray &label);
141

142 143 144
    /**
      Decode part label and namespace.
    */
Guy Maurel's avatar
Guy Maurel committed
145
    static QByteArray decodePartIdentifier(const QByteArray &data, PartNamespace &ns);
146 147 148 149 150

    /**
      Converts the given set of items into a protocol representation.
      @throws A Akonadi::Exception if the item set contains items with missing/invalid identifiers.
    */
151 152
    template<typename T, template<typename> class Container>
    static Scope entitySetToScope(const Container<T> &_objects)
153
    {
Guy Maurel's avatar
Guy Maurel committed
154 155
        if (_objects.isEmpty()) {
            throw Exception("No objects specified");
156
        }
Guy Maurel's avatar
Guy Maurel committed
157

158
        Container<T> objects(_objects);
159 160
        using namespace std::placeholders;
        std::sort(objects.begin(), objects.end(),
Laurent Montel's avatar
Laurent Montel committed
161 162 163
        [](const T & a, const T & b) -> bool {
            return a.id() < b.id();
        });
164
        if (objects.at(0).isValid()) {
Guy Maurel's avatar
Guy Maurel committed
165
            QVector<typename T::Id>  uids;
166 167
            uids.reserve(objects.size());
            for (const T &object : objects) {
Guy Maurel's avatar
Guy Maurel committed
168 169 170 171
                uids << object.id();
            }
            ImapSet set;
            set.add(uids);
172
            return Scope(set);
Guy Maurel's avatar
Guy Maurel committed
173 174
        }

175 176 177 178 179
        if (entitySetHasGID(_objects)) {
            return entitySetToGID(_objects);
        }

        if (!entitySetHasRemoteIdentifier(_objects, std::mem_fn(&T::remoteId))) {
Guy Maurel's avatar
Guy Maurel committed
180 181 182 183
            throw Exception("No remote identifier specified");
        }

        // check if we have RIDs or HRIDs
184
        if (entitySetHasHRID(_objects)) {
185
            return hierarchicalRidToScope(objects.first());
Guy Maurel's avatar
Guy Maurel committed
186 187
        }

188
        return entitySetToRemoteIdentifier(Scope::Rid, _objects, std::mem_fn(&T::remoteId));
189
    }
190

191
    static Protocol::ScopeContext commandContextToProtocol(const Akonadi::Collection &collection, const Akonadi::Tag &tag,
Laurent Montel's avatar
Laurent Montel committed
192
            const Item::List &requestedItems);
193

Volker Krause's avatar
Volker Krause committed
194 195 196 197 198
    /**
      Converts the given object identifier into a protocol representation.
      @throws A Akonadi::Exception if the item set contains items with missing/invalid identifiers.
    */
    template <typename T>
199
    static Scope entityToScope(const T &object)
Volker Krause's avatar
Volker Krause committed
200
    {
201
        return entitySetToScope(QVector<T>() << object);
Volker Krause's avatar
Volker Krause committed
202 203
    }

204 205 206 207
    /**
      Converts the given collection's hierarchical RID into a protocol representation.
      Assumes @p col has a valid hierarchical RID, so check that before!
    */
208
    static Scope hierarchicalRidToScope(const Collection &col);
209

Volker Krause's avatar
Volker Krause committed
210 211 212 213
    /**
      Converts the HRID of the given item into an ASAP protocol representation.
      Assumes @p item has a valid HRID.
    */
214
    static Scope hierarchicalRidToScope(const Item &item);
Volker Krause's avatar
Volker Krause committed
215

216 217 218 219 220 221
    static Scope hierarchicalRidToScope(const Tag &/*tag*/)
    {
        assert(false);
        return Scope();
    }

222 223 224
    /**
      Converts a given ItemFetchScope object into a protocol representation.
    */
225
    static Protocol::FetchScope itemFetchScopeToProtocol(const ItemFetchScope &fetchScope);
226

Christian Mollekopf's avatar
Christian Mollekopf committed
227 228 229
    /**
      Converts a given TagFetchScope object into a protocol representation.
    */
230
    static QVector<QByteArray> tagFetchScopeToProtocol(const TagFetchScope &fetchScope);
Christian Mollekopf's avatar
Christian Mollekopf committed
231

232 233 234
    /**
      Parses a single line from an item fetch job result into an Item object.
     */
Laurent Montel's avatar
Laurent Montel committed
235
    static Item parseItemFetchResult(const Protocol::FetchItemsResponse &data, ProtocolHelperValuePool *valuePool = nullptr);
236 237
    static Tag parseTagFetchResult(const Protocol::FetchTagsResponse &data);
    static Relation parseRelationFetchResult(const Protocol::FetchRelationsResponse &data);
Christian Mollekopf's avatar
Christian Mollekopf committed
238

239
    static bool streamPayloadToFile(const QString &file, const QByteArray &data, QByteArray &error);
240

241
    static Akonadi::Tristate listPreference(const Collection::ListPreference pref);
242 243 244 245

private:
    template<typename T, template<typename> class Container>
    inline static
Laurent Montel's avatar
Laurent Montel committed
246
    typename std::enable_if < !std::is_same<T, Akonadi::Collection>::value, bool >::type
247 248 249 250 251 252 253 254
    entitySetHasGID(const Container<T> &objects)
    {
        return entitySetHasRemoteIdentifier(objects, std::mem_fn(&T::gid));
    }

    template<typename T, template<typename> class Container>
    inline static
    typename std::enable_if<std::is_same<T, Akonadi::Collection>::value, bool>::type
Laurent Montel's avatar
Laurent Montel committed
255
    entitySetHasGID(const Container<T> &/*objects*/, int * /*dummy*/ = nullptr)
256 257 258 259 260 261
    {
        return false;
    }

    template<typename T, template<typename> class Container>
    inline static
Laurent Montel's avatar
Laurent Montel committed
262
    typename std::enable_if < !std::is_same<T, Akonadi::Collection>::value, Scope >::type
263 264 265 266 267 268 269 270
    entitySetToGID(const Container<T> &objects)
    {
        return entitySetToRemoteIdentifier(Scope::Gid, objects, std::mem_fn(&T::gid));
    }

    template<typename T, template<typename> class Container>
    inline static
    typename std::enable_if<std::is_same<T, Akonadi::Collection>::value, Scope>::type
Laurent Montel's avatar
Laurent Montel committed
271
    entitySetToGID(const Container<T> &/*objects*/, int * /*dummy*/ = nullptr)
272 273 274 275 276 277 278 279 280
    {
        return Scope();
    }

    template<typename T, template<typename> class Container, typename RIDFunc>
    inline static
    bool entitySetHasRemoteIdentifier(const Container<T> &objects, const RIDFunc &ridFunc)
    {
        return std::find_if(objects.constBegin(), objects.constEnd(),
Laurent Montel's avatar
Laurent Montel committed
281 282 283 284
        [ = ](const T & obj) {
            return ridFunc(obj).isEmpty();
        })
        == objects.constEnd();
285 286 287 288 289 290 291 292 293 294
    }

    template<typename T, template<typename> class Container, typename RIDFunc>
    inline static
    typename std::enable_if<std::is_same<QString, typename RIDFunc:: result_type>::value, Scope>::type
    entitySetToRemoteIdentifier(Scope::SelectionScope scope, const Container<T> &objects, const RIDFunc &ridFunc)
    {
        QStringList rids;
        rids.reserve(objects.size());
        std::transform(objects.cbegin(), objects.cend(),
Laurent Montel's avatar
Laurent Montel committed
295 296 297
        std::back_inserter(rids), [ = ](const T & obj) -> QString {
            return ridFunc(obj);
        });
298 299 300 301 302 303
        return Scope(scope, rids);
    }

    template<typename T, template<typename> class Container, typename RIDFunc>
    inline static
    typename std::enable_if<std::is_same<QByteArray, typename RIDFunc:: result_type>::value, Scope>::type
Laurent Montel's avatar
Laurent Montel committed
304
    entitySetToRemoteIdentifier(Scope::SelectionScope scope, const Container<T> &objects, const RIDFunc &ridFunc, int * /*dummy*/ = nullptr)
305 306 307 308
    {
        QStringList rids;
        rids.reserve(objects.size());
        std::transform(objects.cbegin(), objects.cend(),
Laurent Montel's avatar
Laurent Montel committed
309 310 311
        std::back_inserter(rids), [ = ](const T & obj) -> QString {
            return QString::fromLatin1(ridFunc(obj));
        });
312 313 314 315 316
        return Scope(scope, rids);
    }

    template<typename T, template<typename> class Container>
    inline static
Laurent Montel's avatar
Laurent Montel committed
317
    typename std::enable_if < !std::is_same<T, Tag>::value, bool >::type
318 319 320
    entitySetHasHRID(const Container<T> &objects)
    {
        return objects.size() == 1 &&
Laurent Montel's avatar
Laurent Montel committed
321
               std::find_if(objects.constBegin(), objects.constEnd(),
Laurent Montel's avatar
Laurent Montel committed
322 323 324 325
        [](const T & obj) -> bool {
            return !CollectionUtils::hasValidHierarchicalRID(obj);
        })
        == objects.constEnd();  // ### HRID sets are not yet specified
326 327 328 329 330
    }

    template<typename T, template<typename> class Container>
    inline static
    typename std::enable_if<std::is_same<T, Tag>::value, bool>::type
Laurent Montel's avatar
Laurent Montel committed
331
    entitySetHasHRID(const Container<T> &/*objects*/, int * /*dummy*/ = nullptr)
332 333 334
    {
        return false;
    }
335 336 337 338 339
};

}

#endif