File indexing completed on 2025-01-05 04:46:55

0001 /*
0002     SPDX-FileCopyrightText: 2017 Daniel Vrátil <dvratil@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include "akthread.h"
0010 #include "entities.h"
0011 
0012 #include "private/tristate_p.h"
0013 
0014 #include <QHash>
0015 #include <QList>
0016 #include <QReadWriteLock>
0017 
0018 namespace Akonadi
0019 {
0020 class Scope;
0021 
0022 namespace Server
0023 {
0024 class CommandContext;
0025 
0026 class CollectionTreeCache : public AkThread
0027 {
0028     Q_OBJECT
0029 
0030 protected:
0031     class Node
0032     {
0033     public:
0034         explicit Node();
0035         explicit Node(const Collection &query);
0036 
0037         ~Node();
0038 
0039         void appendChild(Node *child);
0040         void removeChild(Node *child);
0041 
0042         Node *parent = nullptr;
0043         QList<Node *> children;
0044         QAtomicInt lruCounter;
0045         qint64 id;
0046 
0047         Collection collection;
0048     };
0049 
0050 public:
0051     explicit CollectionTreeCache();
0052     ~CollectionTreeCache() override;
0053 
0054     QList<Collection>
0055     retrieveCollections(const Scope &scope, int depth, int ancestorDepth, const QString &resource = QString(), CommandContext *context = nullptr) const;
0056 
0057 public Q_SLOTS:
0058     void collectionAdded(const Collection &col);
0059     void collectionChanged(const Collection &col);
0060     void collectionMoved(const Collection &col);
0061     void collectionRemoved(const Collection &col);
0062 
0063 protected:
0064     void init() override;
0065     void quit() override;
0066 
0067     Node *findNode(const QString &rid, const QString &resource) const;
0068 
0069     template<typename Predicate>
0070     Node *findNode(Node *root, Predicate pred) const;
0071 
0072     QList<Collection> retrieveCollections(Node *root, int depth, int ancestorDepth) const;
0073 
0074 protected:
0075     mutable QReadWriteLock mLock;
0076 
0077     Node *mRoot = nullptr;
0078 
0079     QHash<qint64 /* col ID */, Node *> mNodeLookup;
0080 };
0081 
0082 // Non-recursive depth-first tree traversal, looking for first Node matching the predicate
0083 template<typename Predicate>
0084 CollectionTreeCache::Node *CollectionTreeCache::findNode(Node *root, Predicate pred) const
0085 {
0086     QList<Node *> toVisit = {root};
0087     // We expect a single subtree to not contain more than 1/4 of all collections,
0088     // which is an arbitrary guess, but should be good enough for most cases.
0089     toVisit.reserve(mNodeLookup.size() / 4);
0090     while (!toVisit.isEmpty()) {
0091         auto node = toVisit.takeFirst();
0092         if (pred(node)) {
0093             return node;
0094         }
0095 
0096         for (auto child : std::as_const(node->children)) {
0097             toVisit.prepend(child);
0098         }
0099     }
0100 
0101     return nullptr;
0102 }
0103 
0104 } // namespace Server
0105 } // namespace Akonadi