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 #include "entitydisplayattribute.h"
16 
17 #include "akonadicore_debug.h"
18 
19 using namespace Akonadi;
20 
21 /**
22  @internal
23 */
24 class Akonadi::SpecialCollectionsRequestJobPrivate
25 {
26 public:
27  SpecialCollectionsRequestJobPrivate(SpecialCollections *collections, SpecialCollectionsRequestJob *qq);
28 
29  bool isEverythingReady() const;
30  void lockResult(KJob *job); // slot
31  void releaseLock(); // slot
32  void nextResource();
33  void resourceScanResult(KJob *job); // slot
34  void createRequestedFolders(ResourceScanJob *job, QHash<QByteArray, bool> &requestedFolders);
35  void collectionCreateResult(KJob *job); // slot
36 
38  SpecialCollections *mSpecialCollections = nullptr;
39  int mPendingCreateJobs = 0;
40 
41  QByteArray mRequestedType;
42  AgentInstance mRequestedResource;
43 
44  // Input:
45  QHash<QByteArray, bool> mDefaultFolders;
46  bool mRequestingDefaultFolders = false;
47  QHash<QString, QHash<QByteArray, bool>> mFoldersForResource;
48  QString mDefaultResourceType;
49  QVariantMap mDefaultResourceOptions;
50  QList<QByteArray> mKnownTypes;
51  QMap<QByteArray, QString> mNameForTypeMap;
52  QMap<QByteArray, QString> mIconForTypeMap;
53 
54  // Output:
55  QStringList mToForget;
57 };
58 
59 SpecialCollectionsRequestJobPrivate::SpecialCollectionsRequestJobPrivate(SpecialCollections *collections, SpecialCollectionsRequestJob *qq)
60  : q(qq)
61  , mSpecialCollections(collections)
62 {
63 }
64 
65 bool SpecialCollectionsRequestJobPrivate::isEverythingReady() const
66 {
67  // check if all requested folders are known already
68  if (mRequestingDefaultFolders) {
69  for (auto it = mDefaultFolders.cbegin(), end = mDefaultFolders.cend(); it != end; ++it) {
70  if (it.value() && !mSpecialCollections->hasDefaultCollection(it.key())) {
71  return false;
72  }
73  }
74  }
75 
76  for (auto resourceIt = mFoldersForResource.cbegin(), end = mFoldersForResource.cend(); resourceIt != end; ++resourceIt) {
77  const QHash<QByteArray, bool> &requested = resourceIt.value();
78  for (auto it = requested.cbegin(), end = requested.cend(); it != end; ++it) {
79  if (it.value() && !mSpecialCollections->hasCollection(it.key(), AgentManager::self()->instance(resourceIt.key()))) {
80  return false;
81  }
82  }
83  }
84 
85  return true;
86 }
87 
88 void SpecialCollectionsRequestJobPrivate::lockResult(KJob *job)
89 {
90  if (job->error()) {
91  qCWarning(AKONADICORE_LOG) << "Failed to get lock:" << job->errorString();
92  q->setError(job->error());
93  q->setErrorText(job->errorString());
94  q->emitResult();
95  return;
96  }
97 
98  if (mRequestingDefaultFolders) {
99  // If default folders are requested, deal with that first.
100  auto resjob = new DefaultResourceJob(mSpecialCollections->d->mSettings, q);
101  resjob->setDefaultResourceType(mDefaultResourceType);
102  resjob->setDefaultResourceOptions(mDefaultResourceOptions);
103  resjob->setTypes(mKnownTypes);
104  resjob->setNameForTypeMap(mNameForTypeMap);
105  resjob->setIconForTypeMap(mIconForTypeMap);
106  QObject::connect(resjob, &KJob::result, q, [this](KJob *job) {
107  resourceScanResult(job);
108  });
109  } else {
110  // If no default folders are requested, go straight to the next step.
111  nextResource();
112  }
113 }
114 
115 void SpecialCollectionsRequestJobPrivate::releaseLock()
116 {
117  const bool ok = Akonadi::releaseLock();
118  if (!ok) {
119  qCWarning(AKONADICORE_LOG) << "WTF, can't release lock.";
120  }
121 }
122 
123 void SpecialCollectionsRequestJobPrivate::nextResource()
124 {
125  if (mFoldersForResource.isEmpty()) {
126  qCDebug(AKONADICORE_LOG) << "All done! Committing.";
127 
128  mSpecialCollections->d->beginBatchRegister();
129 
130  // Forget everything we knew before about these resources.
131  for (const QString &resourceId : std::as_const(mToForget)) {
132  mSpecialCollections->d->forgetFoldersForResource(resourceId);
133  }
134 
135  // Register all the collections that we fetched / created.
136  using RegisterPair = QPair<Collection, QByteArray>;
137  for (const RegisterPair &pair : std::as_const(mToRegister)) {
138  const bool ok = mSpecialCollections->registerCollection(pair.second, pair.first);
139  Q_ASSERT(ok);
140  Q_UNUSED(ok)
141  }
142 
143  mSpecialCollections->d->endBatchRegister();
144 
145  // Release the lock once the transaction has been committed.
146  QObject::connect(q, &KJob::result, q, [this]() {
147  releaseLock();
148  });
149 
150  // We are done!
151  q->commit();
152 
153  } else {
154  const QString resourceId = mFoldersForResource.cbegin().key();
155  qCDebug(AKONADICORE_LOG) << "A resource is done," << mFoldersForResource.count() << "more to do. Now doing resource" << resourceId;
156  auto resjob = new ResourceScanJob(resourceId, mSpecialCollections->d->mSettings, q);
157  QObject::connect(resjob, &KJob::result, q, [this](KJob *job) {
158  resourceScanResult(job);
159  });
160  }
161 }
162 
163 void SpecialCollectionsRequestJobPrivate::resourceScanResult(KJob *job)
164 {
165  auto resjob = qobject_cast<ResourceScanJob *>(job);
166  Q_ASSERT(resjob);
167 
168  const QString resourceId = resjob->resourceId();
169  qCDebug(AKONADICORE_LOG) << "resourceId" << resourceId;
170 
171  if (job->error()) {
172  qCWarning(AKONADICORE_LOG) << "Failed to request resource" << resourceId << ":" << job->errorString();
173  return;
174  }
175 
176  if (qobject_cast<DefaultResourceJob *>(job)) {
177  // This is the default resource.
178  if (resourceId != mSpecialCollections->d->defaultResourceId()) {
179  qCWarning(AKONADICORE_LOG) << "Resource id's don't match: " << resourceId << mSpecialCollections->d->defaultResourceId();
180  Q_ASSERT(false);
181  }
182  // mToForget.append( mSpecialCollections->defaultResourceId() );
183  createRequestedFolders(resjob, mDefaultFolders);
184  } else {
185  // This is not the default resource.
186  QHash<QByteArray, bool> requestedFolders = mFoldersForResource[resourceId];
187  mFoldersForResource.remove(resourceId);
188  createRequestedFolders(resjob, requestedFolders);
189  }
190 }
191 
192 void SpecialCollectionsRequestJobPrivate::createRequestedFolders(ResourceScanJob *scanJob, QHash<QByteArray, bool> &requestedFolders)
193 {
194  // Remove from the request list the folders which already exist.
195  const Akonadi::Collection::List lstSpecialCols = scanJob->specialCollections();
196  for (const Collection &collection : lstSpecialCols) {
197  Q_ASSERT(collection.hasAttribute<SpecialCollectionAttribute>());
198  const auto attr = collection.attribute<SpecialCollectionAttribute>();
199  const QByteArray type = attr->collectionType();
200 
201  if (!type.isEmpty()) {
202  mToRegister.append(qMakePair(collection, type));
203  requestedFolders.insert(type, false);
204  }
205  }
206  mToForget.append(scanJob->resourceId());
207 
208  // Folders left in the request list must be created.
209  Q_ASSERT(mPendingCreateJobs == 0);
210  Q_ASSERT(scanJob->rootResourceCollection().isValid());
211 
212  QHashIterator<QByteArray, bool> it(requestedFolders);
213  while (it.hasNext()) {
214  it.next();
215 
216  if (it.value()) {
217  Collection collection;
218  collection.setParentCollection(scanJob->rootResourceCollection());
219  collection.setName(mNameForTypeMap.value(it.key()));
220 
221  setCollectionAttributes(collection, it.key(), mNameForTypeMap, mIconForTypeMap);
222 
223  auto createJob = new CollectionCreateJob(collection, q);
224  createJob->setProperty("type", it.key());
225  QObject::connect(createJob, &KJob::result, q, [this](KJob *job) {
226  collectionCreateResult(job);
227  });
228 
229  mPendingCreateJobs++;
230  }
231  }
232 
233  if (mPendingCreateJobs == 0) {
234  nextResource();
235  }
236 }
237 
238 void SpecialCollectionsRequestJobPrivate::collectionCreateResult(KJob *job)
239 {
240  if (job->error()) {
241  qCWarning(AKONADICORE_LOG) << "Failed CollectionCreateJob." << job->errorString();
242  return;
243  }
244 
245  auto createJob = qobject_cast<CollectionCreateJob *>(job);
246  Q_ASSERT(createJob);
247 
248  const Collection collection = createJob->collection();
249  mToRegister.append(qMakePair(collection, createJob->property("type").toByteArray()));
250 
251  Q_ASSERT(mPendingCreateJobs > 0);
252  mPendingCreateJobs--;
253  qCDebug(AKONADICORE_LOG) << "mPendingCreateJobs now" << mPendingCreateJobs;
254 
255  if (mPendingCreateJobs == 0) {
256  nextResource();
257  }
258 }
259 
260 // TODO KDE5: do not inherit from TransactionSequence
262  : TransactionSequence(parent)
263  , d(new SpecialCollectionsRequestJobPrivate(collections, this))
264 {
265  setProperty("transactionsDisabled", true);
266 }
267 
269 
271 {
272  d->mDefaultFolders[type] = true;
273  d->mRequestingDefaultFolders = true;
274  d->mRequestedType = type;
275 }
276 
278 {
279  d->mFoldersForResource[instance.identifier()][type] = true;
280  d->mRequestedType = type;
281  d->mRequestedResource = instance;
282 }
283 
285 {
286  if (d->mRequestedResource.isValid()) {
287  return d->mSpecialCollections->collection(d->mRequestedType, d->mRequestedResource);
288  } else {
289  return d->mSpecialCollections->defaultCollection(d->mRequestedType);
290  }
291 }
292 
294 {
295  d->mDefaultResourceType = type;
296 }
297 
299 {
300  d->mDefaultResourceOptions = options;
301 }
302 
304 {
305  d->mKnownTypes = types;
306 }
307 
309 {
310  d->mNameForTypeMap = map;
311 }
312 
314 {
315  d->mIconForTypeMap = map;
316 }
317 
319 {
320  if (d->isEverythingReady()) {
321  emitResult();
322  } else {
323  auto lockJob = new GetLockJob(this);
324  connect(lockJob, &GetLockJob::result, this, [this](KJob *job) {
325  d->lockResult(job);
326  });
327  lockJob->start();
328  }
329 }
330 
331 void SpecialCollectionsRequestJob::slotResult(KJob *job)
332 {
333  if (job->error()) {
334  // If we failed, let others try.
335  qCWarning(AKONADICORE_LOG) << "Failed SpecialCollectionsRequestJob::slotResult" << job->errorString();
336 
337  d->releaseLock();
338  }
339  TransactionSequence::slotResult(job);
340 }
341 
342 #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-2022 The KDE developers.
Generated on Sat Jun 25 2022 06:00:33 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.