File indexing completed on 2024-11-10 04:40:39
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"