7#include "collectiontreecache.h" 
    8#include "akonadiserver_debug.h" 
    9#include "commandcontext.h" 
   10#include "selectquerybuilder.h" 
   12#include "private/scope_p.h" 
   21using namespace Akonadi::Server;
 
   38CollectionTreeCache::Node::Node()
 
   45CollectionTreeCache::Node::Node(
const Collection &col)
 
   53CollectionTreeCache::Node::~Node()
 
   58void CollectionTreeCache::Node::appendChild(Node *child)
 
   64void CollectionTreeCache::Node::removeChild(Node *child)
 
   66    child->parent = 
nullptr;
 
   70CollectionTreeCache::CollectionTreeCache()
 
   71    : AkThread(QStringLiteral(
"CollectionTreeCache"))
 
   75CollectionTreeCache::~CollectionTreeCache()
 
   80void CollectionTreeCache::init()
 
   84    QWriteLocker locker(&mLock);
 
   88    mRoot->parent = 
nullptr;
 
   89    mNodeLookup.insert(0, mRoot);
 
   91    SelectQueryBuilder<Collection> qb;
 
   92    qb.
addSortColumn(Collection::idFullColumnName(), Query::Ascending);
 
   95        qCCritical(AKONADISERVER_LOG) << 
"Failed to initialize Collection tree cache!";
 
  100    std::multimap<qint64 , Node *> pendingNodes;
 
  101    const auto collections = qb.
result();
 
  102    for (
const auto &col : collections) {
 
  103        auto parent = mNodeLookup.value(col.parentId(), 
nullptr);
 
  105        auto node = 
new Node(col);
 
  108            parent->appendChild(node);
 
  109            mNodeLookup.insert(node->id, node);
 
  111            pendingNodes.insert({col.parentId(), node});
 
  115    if (!pendingNodes.empty()) {
 
  126        auto it = pendingNodes.rbegin();
 
  127        while (!pendingNodes.empty()) {
 
  128            auto parent = mNodeLookup.value(it->first, 
nullptr);
 
  134                auto node = it->second;
 
  135                parent->appendChild(node);
 
  136                mNodeLookup.insert(node->id, node);
 
  137                pendingNodes.erase((++it).base());
 
  141            if (it == pendingNodes.rend()) {
 
  142                if (Q_UNLIKELY(inserts == 0)) {
 
  149                    qCWarning(AKONADISERVER_LOG) << 
"Found unreferenced Collections!";
 
  150                    auto unref = pendingNodes.begin();
 
  151                    while (unref != pendingNodes.end()) {
 
  152                        qCWarning(AKONADISERVER_LOG) << 
"\tCollection" << unref->second->id << 
"references an invalid parent" << it->first;
 
  154                        delete unref->second;
 
  155                        unref = pendingNodes.erase(unref);
 
  157                    qCWarning(AKONADISERVER_LOG) << 
"Please run \"akonadictl fsck\" to correct the inconsistencies!";
 
  162                it = pendingNodes.rbegin();
 
  168    Q_ASSERT(pendingNodes.empty());
 
  169    Q_ASSERT(mNodeLookup.size() == collections.count() + 1 );
 
  173void CollectionTreeCache::quit()
 
  180void CollectionTreeCache::collectionAdded(
const Collection &col)
 
  182    QWriteLocker locker(&mLock);
 
  184    auto parent = mNodeLookup.value(col.parentId(), 
nullptr);
 
  186        qCWarning(AKONADISERVER_LOG) << 
"Received a new collection (" << col.id() << 
") with unknown parent (" << col.parentId() << 
")";
 
  190    auto node = 
new Node(col);
 
  191    parent->appendChild(node);
 
  192    mNodeLookup.insert(node->id, node);
 
  195void CollectionTreeCache::collectionChanged(
const Collection &col)
 
  197    QWriteLocker locker(&mLock);
 
  199    auto node = mNodeLookup.value(col.id(), 
nullptr);
 
  201        qCWarning(AKONADISERVER_LOG) << 
"Received an unknown changed collection (" << col.id() << 
")";
 
  206    if (node->collection.isValid()) {
 
  207        node->collection = col;
 
  211void CollectionTreeCache::collectionMoved(
const Collection &col)
 
  213    QWriteLocker locker(&mLock);
 
  215    auto node = mNodeLookup.value(col.id(), 
nullptr);
 
  217        qCWarning(AKONADISERVER_LOG) << 
"Received an unknown moved collection (" << col.id() << 
")";
 
  220    auto oldParent = node->parent;
 
  222    auto newParent = mNodeLookup.value(col.parentId(), 
nullptr);
 
  224        qCWarning(AKONADISERVER_LOG) << 
"Received a moved collection (" << col.id() << 
") with an unknown move destination (" << col.parentId() << 
")";
 
  228    oldParent->removeChild(node);
 
  229    newParent->appendChild(node);
 
  230    if (node->collection.isValid()) {
 
  231        node->collection = col;
 
  235void CollectionTreeCache::collectionRemoved(
const Collection &col)
 
  237    QWriteLocker locker(&mLock);
 
  239    auto node = mNodeLookup.value(col.id(), 
nullptr);
 
  241        qCWarning(AKONADISERVER_LOG) << 
"Received unknown removed collection (" << col.id() << 
")";
 
  245    auto parent = node->parent;
 
  246    parent->removeChild(node);
 
  247    mNodeLookup.remove(node->id);
 
  251CollectionTreeCache::Node *CollectionTreeCache::findNode(
const QString &rid, 
const QString &resource)
 const 
  253    QReadLocker locker(&mLock);
 
  256    auto root = std::find_if(mRoot->children.cbegin(), mRoot->children.cend(), [resource](Node *node) {
 
  260        return node->collection.resource().name() == resource;
 
  262    if (root == mRoot->children.cend()) {
 
  266    return findNode((*root), [rid](Node *node) {
 
  267        return node->collection.remoteId() == rid;
 
  271QList<Collection> CollectionTreeCache::retrieveCollections(CollectionTreeCache::Node *root, 
int depth, 
int ancestorDepth)
 const 
  273    QReadLocker locker(&mLock);
 
  277    Node *
parent = root->parent;
 
  278    for (
int i = 0; i < ancestorDepth && 
parent != 
nullptr; ++i) {
 
  287    QStack<StackTuple> stack;
 
  288    stack.
push({root, 0});
 
  290        auto c = stack.
pop();
 
  291        if (c.depth > depth) {
 
  295        if (c.node->id > 0) { 
 
  299        for (
auto child : std::as_const(c.node->children)) {
 
  300            stack.
push({child, c.depth + 1});
 
  304    QList<Collection> cols;
 
  305    QList<Node *> missing;
 
  306    for (
auto node : nodes) {
 
  307        if (node->collection.isValid()) {
 
  316        SelectQueryBuilder<Collection> qb;
 
  317        Query::Condition cond(Query::Or);
 
  318        for (
auto node : std::as_const(missing)) {
 
  319            cond.addValueCondition(Collection::idFullColumnName(), Query::Equals, node->id);
 
  323            qCWarning(AKONADISERVER_LOG) << 
"Failed to retrieve collections from the database";
 
  327        const auto results = qb.
result();
 
  328        if (results.size() != missing.
size()) {
 
  329            qCWarning(AKONADISERVER_LOG) << 
"Could not obtain all missing collections! Node tree refers to a non-existent collection";
 
  337        QWriteLocker wLocker(&mLock);
 
  338        for (
auto node : std::as_const(missing)) {
 
  339            auto it = std::find_if(results.cbegin(), results.cend(), [node](
const Collection &col) {
 
  340                return node->id == col.id();
 
  342            if (Q_UNLIKELY(it == results.cend())) {
 
  346            node->collection = *it;
 
  354CollectionTreeCache::retrieveCollections(
const Scope &scope, 
int depth, 
int ancestorDepth, 
const QString &resource, CommandContext *context)
 const 
  356    if (scope.isEmpty()) {
 
  357        return retrieveCollections(mRoot, depth, ancestorDepth);
 
  358    } 
else if (scope.scope() == Scope::Rid) {
 
  360        Q_ASSERT(!resource.
isEmpty() || (context && context->resource().isValid()));
 
  362        Node *node = 
nullptr;
 
  364            node = findNode(scope.rid(), resource);
 
  365        } 
else if (context && context->resource().isValid()) {
 
  366            node = findNode(scope.rid(), context->resource().
name());
 
  371        if (Q_LIKELY(node)) {
 
  372            return retrieveCollections(node, depth, ancestorDepth);
 
  374    } 
else if (scope.scope() == Scope::Uid) {
 
  375        Node *node = mNodeLookup.value(scope.uid());
 
  376        if (Q_LIKELY(node)) {
 
  377            return retrieveCollections(node, depth, ancestorDepth);
 
  384#include "moc_collectiontreecache.cpp" 
Represents a collection of PIM items.
 
void addSortColumn(const QString &column, Query::SortOrder order=Query::Ascending)
Add sort column.
 
bool exec()
Executes the query, returns true on success.
 
void addCondition(const Query::Condition &condition, ConditionType type=WhereCondition)
Add a WHERE condition.
 
QList< T > result()
Returns the result of this SELECT query.
 
bool isEmpty() const const
 
void push_back(parameter_type value)
 
qsizetype size() const const
 
const QObjectList & children() const const
 
QObject * parent() const const
 
bool isEmpty() const const