Akonadi

specialcollectionsrequestjob.cpp
1 /*
2  SPDX-FileCopyrightText: 2009 Constantin Berzan <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "specialcollectionsrequestjob.h"
8 
9 #include "specialcollectionattribute.h"
10 #include "specialcollections_p.h"
11 #include "specialcollectionshelperjobs_p.h"
12 
13 #include "agentmanager.h"
14 #include "collectioncreatejob.h"
15 
16 #include "akonadicore_debug.h"
17 
18 using namespace Akonadi;
19 
20 /**
21  @internal
22 */
23 class Akonadi::SpecialCollectionsRequestJobPrivate
24 {
25 public:
26  SpecialCollectionsRequestJobPrivate(SpecialCollections *collections, SpecialCollectionsRequestJob *qq);
27 
28  bool isEverythingReady() const;
29  void lockResult(KJob *job); // slot
30  void releaseLock(); // slot
31  void nextResource();
32  void resourceScanResult(KJob *job); // slot
33  void createRequestedFolders(ResourceScanJob *job, QHash<QByteArray, bool> &requestedFolders);
34  void collectionCreateResult(KJob *job); // slot
35 
37  SpecialCollections *mSpecialCollections = nullptr;
38  int mPendingCreateJobs = 0;
39 
40  QByteArray mRequestedType;
41  AgentInstance mRequestedResource;
42 
43  // Input:
44  QHash<QByteArray, bool> mDefaultFolders;
45  bool mRequestingDefaultFolders = false;
46  QHash<QString, QHash<QByteArray, bool>> mFoldersForResource;
47  QString mDefaultResourceType;
48  QVariantMap mDefaultResourceOptions;
49  QList<QByteArray> mKnownTypes;
50  QMap<QByteArray, QString> mNameForTypeMap;
51  QMap<QByteArray, QString> mIconForTypeMap;
52 
53  // Output:
54  QStringList mToForget;
56 };
57 
58 SpecialCollectionsRequestJobPrivate::SpecialCollectionsRequestJobPrivate(SpecialCollections *collections, SpecialCollectionsRequestJob *qq)
59  : q(qq)
60  , mSpecialCollections(collections)
61 {
62 }
63 
64 bool SpecialCollectionsRequestJobPrivate::isEverythingReady() const
65 {
66  // check if all requested folders are known already
67  if (mRequestingDefaultFolders) {
68  for (auto it = mDefaultFolders.cbegin(), end = mDefaultFolders.cend(); it != end; ++it) {
69  if (it.value() && !mSpecialCollections->hasDefaultCollection(it.key())) {
70  return false;
71  }
72  }
73  }
74 
75  for (auto resourceIt = mFoldersForResource.cbegin(), end = mFoldersForResource.cend(); resourceIt != end; ++resourceIt) {
76  const QHash<QByteArray, bool> &requested = resourceIt.value();
77  for (auto it = requested.cbegin(), end = requested.cend(); it != end; ++it) {
78  if (it.value() && !mSpecialCollections->hasCollection(it.key(), AgentManager::self()->instance(resourceIt.key()))) {
79  return false;
80  }
81  }
82  }
83 
84  return true;
85 }
86 
87 void SpecialCollectionsRequestJobPrivate::lockResult(KJob *job)
88 {
89  if (job->error()) {
90  qCWarning(AKONADICORE_LOG) << "Failed to get lock:" << job->errorString();
91  q->setError(job->error());
92  q->setErrorText(job->errorString());
93  q->emitResult();
94  return;
95  }
96 
97  if (mRequestingDefaultFolders) {
98  // If default folders are requested, deal with that first.
99  auto resjob = new DefaultResourceJob(mSpecialCollections->d->mSettings, q);
100  resjob->setDefaultResourceType(mDefaultResourceType);
101  resjob->setDefaultResourceOptions(mDefaultResourceOptions);
102  resjob->setTypes(mKnownTypes);
103  resjob->setNameForTypeMap(mNameForTypeMap);
104  resjob->setIconForTypeMap(mIconForTypeMap);
105  QObject::connect(resjob, &KJob::result, q, [this](KJob *job) {
106  resourceScanResult(job);
107  });
108  } else {
109  // If no default folders are requested, go straight to the next step.
110  nextResource();
111  }
112 }
113 
114 void SpecialCollectionsRequestJobPrivate::releaseLock()
115 {
116  const bool ok = Akonadi::releaseLock();
117  if (!ok) {
118  qCWarning(AKONADICORE_LOG) << "WTF, can't release lock.";
119  }
120 }
121 
122 void SpecialCollectionsRequestJobPrivate::nextResource()
123 {
124  if (mFoldersForResource.isEmpty()) {
125  qCDebug(AKONADICORE_LOG) << "All done! Committing.";
126 
127  mSpecialCollections->d->beginBatchRegister();
128 
129  // Forget everything we knew before about these resources.
130  for (const QString &resourceId : std::as_const(mToForget)) {
131  mSpecialCollections->d->forgetFoldersForResource(resourceId);
132  }
133 
134  // Register all the collections that we fetched / created.
135  using RegisterPair = QPair<Collection, QByteArray>;
136  for (const RegisterPair &pair : std::as_const(mToRegister)) {
137  const bool ok = mSpecialCollections->registerCollection(pair.second, pair.first);
138  Q_ASSERT(ok);
139  Q_UNUSED(ok)
140  }
141 
142  mSpecialCollections->d->endBatchRegister();
143 
144  // Release the lock once the transaction has been committed.
145  QObject::connect(q, &KJob::result, q, [this]() {
146  releaseLock();
147  });
148 
149  // We are done!
150  q->commit();
151 
152  } else {
153  const QString resourceId = mFoldersForResource.cbegin().key();
154  qCDebug(AKONADICORE_LOG) << "A resource is done," << mFoldersForResource.count() << "more to do. Now doing resource" << resourceId;
155  auto resjob = new ResourceScanJob(resourceId, mSpecialCollections->d->mSettings, q);
156  QObject::connect(resjob, &KJob::result, q, [this](KJob *job) {
157  resourceScanResult(job);
158  });
159  }
160 }
161 
162 void SpecialCollectionsRequestJobPrivate::resourceScanResult(KJob *job)
163 {
164  auto resjob = qobject_cast<ResourceScanJob *>(job);
165  Q_ASSERT(resjob);
166 
167  const QString resourceId = resjob->resourceId();
168  qCDebug(AKONADICORE_LOG) << "resourceId" << resourceId;
169 
170  if (job->error()) {
171  qCWarning(AKONADICORE_LOG) << "Failed to request resource" << resourceId << ":" << job->errorString();
172  return;
173  }
174 
175  if (qobject_cast<DefaultResourceJob *>(job)) {
176  // This is the default resource.
177  if (resourceId != mSpecialCollections->d->defaultResourceId()) {
178  qCWarning(AKONADICORE_LOG) << "Resource id's don't match: " << resourceId << mSpecialCollections->d->defaultResourceId();
179  Q_ASSERT(false);
180  }
181  // mToForget.append( mSpecialCollections->defaultResourceId() );
182  createRequestedFolders(resjob, mDefaultFolders);
183  } else {
184  // This is not the default resource.
185  QHash<QByteArray, bool> requestedFolders = mFoldersForResource[resourceId];
186  mFoldersForResource.remove(resourceId);
187  createRequestedFolders(resjob, requestedFolders);
188  }
189 }
190 
191 void SpecialCollectionsRequestJobPrivate::createRequestedFolders(ResourceScanJob *scanJob, QHash<QByteArray, bool> &requestedFolders)
192 {
193  // Remove from the request list the folders which already exist.
194  const Akonadi::Collection::List lstSpecialCols = scanJob->specialCollections();
195  for (const Collection &collection : lstSpecialCols) {
196  Q_ASSERT(collection.hasAttribute<SpecialCollectionAttribute>());
197  const auto attr = collection.attribute<SpecialCollectionAttribute>();
198  const QByteArray type = attr->collectionType();
199 
200  if (!type.isEmpty()) {
201  mToRegister.append(qMakePair(collection, type));
202  requestedFolders.insert(type, false);
203  }
204  }
205  mToForget.append(scanJob->resourceId());
206 
207  // Folders left in the request list must be created.
208  Q_ASSERT(mPendingCreateJobs == 0);
209  Q_ASSERT(scanJob->rootResourceCollection().isValid());
210 
211  QHashIterator<QByteArray, bool> it(requestedFolders);
212  while (it.hasNext()) {
213  it.next();
214 
215  if (it.value()) {
216  Collection collection;
217  collection.setParentCollection(scanJob->rootResourceCollection());
218  collection.setName(mNameForTypeMap.value(it.key()));
219 
220  setCollectionAttributes(collection, it.key(), mNameForTypeMap, mIconForTypeMap);
221 
222  auto createJob = new CollectionCreateJob(collection, q);
223  createJob->setProperty("type", it.key());
224  QObject::connect(createJob, &KJob::result, q, [this](KJob *job) {
225  collectionCreateResult(job);
226  });
227 
228  mPendingCreateJobs++;
229  }
230  }
231 
232  if (mPendingCreateJobs == 0) {
233  nextResource();
234  }
235 }
236 
237 void SpecialCollectionsRequestJobPrivate::collectionCreateResult(KJob *job)
238 {
239  if (job->error()) {
240  qCWarning(AKONADICORE_LOG) << "Failed CollectionCreateJob." << job->errorString();
241  return;
242  }
243 
244  auto createJob = qobject_cast<CollectionCreateJob *>(job);
245  Q_ASSERT(createJob);
246 
247  const Collection collection = createJob->collection();
248  mToRegister.append(qMakePair(collection, createJob->property("type").toByteArray()));
249 
250  Q_ASSERT(mPendingCreateJobs > 0);
251  mPendingCreateJobs--;
252  qCDebug(AKONADICORE_LOG) << "mPendingCreateJobs now" << mPendingCreateJobs;
253 
254  if (mPendingCreateJobs == 0) {
255  nextResource();
256  }
257 }
258 
259 // TODO KDE5: do not inherit from TransactionSequence
261  : TransactionSequence(parent)
262  , d(new SpecialCollectionsRequestJobPrivate(collections, this))
263 {
264  setProperty("transactionsDisabled", true);
265 }
266 
268 
270 {
271  d->mDefaultFolders[type] = true;
272  d->mRequestingDefaultFolders = true;
273  d->mRequestedType = type;
274 }
275 
277 {
278  d->mFoldersForResource[instance.identifier()][type] = true;
279  d->mRequestedType = type;
280  d->mRequestedResource = instance;
281 }
282 
284 {
285  if (d->mRequestedResource.isValid()) {
286  return d->mSpecialCollections->collection(d->mRequestedType, d->mRequestedResource);
287  } else {
288  return d->mSpecialCollections->defaultCollection(d->mRequestedType);
289  }
290 }
291 
293 {
294  d->mDefaultResourceType = type;
295 }
296 
298 {
299  d->mDefaultResourceOptions = options;
300 }
301 
303 {
304  d->mKnownTypes = types;
305 }
306 
308 {
309  d->mNameForTypeMap = map;
310 }
311 
313 {
314  d->mIconForTypeMap = map;
315 }
316 
318 {
319  if (d->isEverythingReady()) {
320  emitResult();
321  } else {
322  auto lockJob = new GetLockJob(this);
323  connect(lockJob, &GetLockJob::result, this, [this](KJob *job) {
324  d->lockResult(job);
325  });
326  lockJob->start();
327  }
328 }
329 
330 void SpecialCollectionsRequestJob::slotResult(KJob *job)
331 {
332  if (job->error()) {
333  // If we failed, let others try.
334  qCWarning(AKONADICORE_LOG) << "Failed SpecialCollectionsRequestJob::slotResult" << job->errorString();
335 
336  d->releaseLock();
337  }
338  TransactionSequence::slotResult(job);
339 }
340 
341 #include "moc_specialcollectionsrequestjob.cpp"
SpecialCollectionsRequestJob(SpecialCollections *collections, QObject *parent=nullptr)
Creates a new special collections request job.
QString identifier() const
Returns the unique identifier of the agent instance.
void setTypes(const QList< QByteArray > &types)
Sets the list of well known special collection types.
const T value(const Key &key) const const
void result(KJob *job)
void setDefaultResourceOptions(const QVariantMap &options)
Sets the configuration options that shall be applied to the new resource that is created if the reque...
void doStart() override
This method must be reimplemented in the concrete jobs.
Represents a collection of PIM items.
Definition: collection.h:61
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QHash::const_iterator cend() const const
QHash::iterator insert(const Key &key, const T &value)
AgentInstance instance(const QString &identifier) const
Returns the agent instance with the given identifier or an invalid agent instance if the identifier d...
~SpecialCollectionsRequestJob() override
Destroys the special collections request job.
void requestDefaultCollection(const QByteArray &type)
Requests a special collection of the given type in the default resource.
void setIconForTypeMap(const QMap< QByteArray, QString > &map)
Sets the map of special collection types to icon names.
void setDefaultResourceType(const QString &type)
Sets the type of the resource that shall be created if the requested special collection does not exis...
An Attribute that stores the special collection type of a collection.
Base class for jobs that need to run a sequence of sub-jobs in a transaction.
An interface to special collections.
bool setProperty(const char *name, const QVariant &value)
void setNameForTypeMap(const QMap< QByteArray, QString > &map)
Sets the map of special collection types to display names.
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
int remove(const Key &key)
QString::const_iterator cbegin() const const
static AgentManager * self()
Returns the global instance of the agent manager.
void setParentCollection(const Collection &parent)
Set the parent collection of this object.
Definition: collection.cpp:204
QHash::const_iterator cbegin() const const
Job that creates a new collection in the Akonadi storage.
void emitResult()
virtual QString errorString() const
int error() const
A representation of an agent instance.
void setName(const QString &name)
Sets the i18n'ed name of the collection.
Definition: collection.cpp:221
A job to request SpecialCollections.
Helper integration between Akonadi and Qt.
Collection collection() const
Returns the requested collection.
void requestCollection(const QByteArray &type, const AgentInstance &instance)
Requests a special collection of the given type in the given resource instance.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Dec 5 2023 03:52:45 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.