File indexing completed on 2024-05-26 05:14:09

0001 /*
0002     SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "collectionpathresolver.h"
0008 
0009 #include "collectionfetchjob.h"
0010 #include "job_p.h"
0011 
0012 #include "akonadicore_debug.h"
0013 
0014 #include <KLocalizedString>
0015 
0016 #include <QStringList>
0017 
0018 using namespace Akonadi;
0019 
0020 /// @cond PRIVATE
0021 
0022 class Akonadi::CollectionPathResolverPrivate : public JobPrivate
0023 {
0024 public:
0025     explicit CollectionPathResolverPrivate(CollectionPathResolver *parent)
0026         : JobPrivate(parent)
0027     {
0028     }
0029 
0030     void init(const QString &path, const Collection &rootCollection)
0031     {
0032         Q_Q(CollectionPathResolver);
0033 
0034         mPathToId = true;
0035         mPath = path;
0036         if (mPath.startsWith(q->pathDelimiter())) {
0037             mPath = mPath.right(mPath.length() - q->pathDelimiter().length());
0038         }
0039         if (mPath.endsWith(q->pathDelimiter())) {
0040             mPath = mPath.left(mPath.length() - q->pathDelimiter().length());
0041         }
0042 
0043         mPathParts = splitPath(mPath);
0044         mCurrentNode = rootCollection;
0045     }
0046 
0047     void jobResult(KJob *job);
0048 
0049     QStringList splitPath(const QString &path)
0050     {
0051         if (path.isEmpty()) { // path is normalized, so non-empty means at least one hit
0052             return QStringList();
0053         }
0054 
0055         QStringList rv;
0056         int begin = 0;
0057         const int pathSize(path.size());
0058         for (int i = 0; i < pathSize; ++i) {
0059             if (path[i] == QLatin1Char('/')) {
0060                 QString pathElement = path.mid(begin, i - begin);
0061                 pathElement.replace(QLatin1StringView("\\/"), QLatin1StringView("/"));
0062                 rv.append(pathElement);
0063                 begin = i + 1;
0064             }
0065             if (i < path.size() - 2 && path[i] == QLatin1Char('\\') && path[i + 1] == QLatin1Char('/')) {
0066                 ++i;
0067             }
0068         }
0069         QString pathElement = path.mid(begin);
0070         pathElement.replace(QLatin1StringView("\\/"), QLatin1StringView("/"));
0071         rv.append(pathElement);
0072         return rv;
0073     }
0074 
0075     Q_DECLARE_PUBLIC(CollectionPathResolver)
0076 
0077     Collection mCurrentNode;
0078     QStringList mPathParts;
0079     QString mPath;
0080     Collection::Id mColId = -1;
0081     bool mPathToId = false;
0082 };
0083 
0084 void CollectionPathResolverPrivate::jobResult(KJob *job)
0085 {
0086     if (job->error()) {
0087         return;
0088     }
0089 
0090     Q_Q(CollectionPathResolver);
0091 
0092     auto list = static_cast<CollectionFetchJob *>(job);
0093     CollectionFetchJob *nextJob = nullptr;
0094     const Collection::List cols = list->collections();
0095     if (cols.isEmpty()) {
0096         mColId = -1;
0097         q->setError(CollectionPathResolver::Unknown);
0098         q->setErrorText(i18n("No such collection."));
0099         q->emitResult();
0100         return;
0101     }
0102 
0103     if (mPathToId) {
0104         const QString currentPart = mPathParts.takeFirst();
0105         bool found = false;
0106         for (const Collection &c : cols) {
0107             if (c.name() == currentPart) {
0108                 mCurrentNode = c;
0109                 found = true;
0110                 break;
0111             }
0112         }
0113         if (!found) {
0114             qCWarning(AKONADICORE_LOG) << "No such collection" << currentPart << "with parent" << mCurrentNode.id();
0115             mColId = -1;
0116             q->setError(CollectionPathResolver::Unknown);
0117             q->setErrorText(i18n("No such collection."));
0118             q->emitResult();
0119             return;
0120         }
0121         if (mPathParts.isEmpty()) {
0122             mColId = mCurrentNode.id();
0123             q->emitResult();
0124             return;
0125         }
0126         nextJob = new CollectionFetchJob(mCurrentNode, CollectionFetchJob::FirstLevel, q);
0127     } else {
0128         Collection col = list->collections().at(0);
0129         mCurrentNode = col.parentCollection();
0130         mPathParts.prepend(col.name());
0131         if (mCurrentNode == Collection::root()) {
0132             q->emitResult();
0133             return;
0134         }
0135         nextJob = new CollectionFetchJob(mCurrentNode, CollectionFetchJob::Base, q);
0136     }
0137     q->connect(nextJob, &CollectionFetchJob::result, q, [this](KJob *job) {
0138         jobResult(job);
0139     });
0140 }
0141 
0142 CollectionPathResolver::CollectionPathResolver(const QString &path, QObject *parent)
0143     : Job(new CollectionPathResolverPrivate(this), parent)
0144 {
0145     Q_D(CollectionPathResolver);
0146     d->init(path, Collection::root());
0147 }
0148 
0149 CollectionPathResolver::CollectionPathResolver(const QString &path, const Collection &parentCollection, QObject *parent)
0150     : Job(new CollectionPathResolverPrivate(this), parent)
0151 {
0152     Q_D(CollectionPathResolver);
0153     d->init(path, parentCollection);
0154 }
0155 
0156 CollectionPathResolver::CollectionPathResolver(const Collection &collection, QObject *parent)
0157     : Job(new CollectionPathResolverPrivate(this), parent)
0158 {
0159     Q_D(CollectionPathResolver);
0160 
0161     d->mPathToId = false;
0162     d->mColId = collection.id();
0163     d->mCurrentNode = collection;
0164 }
0165 
0166 CollectionPathResolver::~CollectionPathResolver()
0167 {
0168 }
0169 
0170 Collection::Id CollectionPathResolver::collection() const
0171 {
0172     Q_D(const CollectionPathResolver);
0173 
0174     return d->mColId;
0175 }
0176 
0177 QString CollectionPathResolver::path() const
0178 {
0179     Q_D(const CollectionPathResolver);
0180 
0181     if (d->mPathToId) {
0182         return d->mPath;
0183     }
0184     return d->mPathParts.join(pathDelimiter());
0185 }
0186 
0187 QString CollectionPathResolver::pathDelimiter()
0188 {
0189     return QStringLiteral("/");
0190 }
0191 
0192 void CollectionPathResolver::doStart()
0193 {
0194     Q_D(CollectionPathResolver);
0195 
0196     CollectionFetchJob *job = nullptr;
0197     if (d->mPathToId) {
0198         if (d->mPath.isEmpty()) {
0199             d->mColId = Collection::root().id();
0200             emitResult();
0201             return;
0202         }
0203         job = new CollectionFetchJob(d->mCurrentNode, CollectionFetchJob::FirstLevel, this);
0204     } else {
0205         if (d->mColId == 0) {
0206             d->mColId = Collection::root().id();
0207             emitResult();
0208             return;
0209         }
0210         job = new CollectionFetchJob(d->mCurrentNode, CollectionFetchJob::Base, this);
0211     }
0212     connect(job, &CollectionFetchJob::result, this, [d](KJob *job) {
0213         d->jobResult(job);
0214     });
0215 }
0216 
0217 /// @endcond
0218 
0219 #include "moc_collectionpathresolver.cpp"