20 #include "collectionsync_p.h"
21 #include "collection.h"
23 #include "collectioncreatejob.h"
24 #include "collectiondeletejob.h"
25 #include "collectionfetchjob.h"
26 #include "collectionmodifyjob.h"
27 #include "collectionfetchscope.h"
28 #include "collectionmovejob.h"
32 #include <QtCore/QVariant>
34 using namespace Akonadi;
50 qDeleteAll( childNodes );
51 qDeleteAll( pendingRemoteNodes );
55 QList<LocalNode*> childNodes;
56 QHash<QString, LocalNode*> childRidMap;
60 QList<RemoteNode*> pendingRemoteNodes;
64 Q_DECLARE_METATYPE( LocalNode* )
65 static const
char LOCAL_NODE[] = "LocalNode";
80 Q_DECLARE_METATYPE( RemoteNode* )
81 static const
char REMOTE_NODE[] = "RemoteNode";
95 hierarchicalRIDs( false ),
96 localListDone( false ),
100 localRoot->processed =
true;
101 localUidMap.insert( localRoot->collection.id(), localRoot );
102 if ( !hierarchicalRIDs )
103 localRidMap.insert( QString(), localRoot );
112 LocalNode* createLocalNode(
const Collection &col )
114 LocalNode *node =
new LocalNode( col );
115 Q_ASSERT( !localUidMap.contains( col.
id() ) );
116 localUidMap.insert( node->collection.id(), node );
117 if ( !hierarchicalRIDs && !col.
remoteId().isEmpty() )
118 localRidMap.insert( node->collection.remoteId(), node );
121 if ( localPendingCollections.contains( col.
id() ) ) {
122 QVector<Collection::Id> childIds = localPendingCollections.take( col.
id() );
124 Q_ASSERT( localUidMap.contains( childId ) );
125 LocalNode *childNode = localUidMap.value( childId );
126 node->childNodes.append( childNode );
127 if ( !childNode->collection.remoteId().isEmpty() )
128 node->childRidMap.insert( childNode->collection.remoteId(), childNode );
135 parentNode->childNodes.append( node );
136 if ( !node->collection.remoteId().isEmpty() )
137 parentNode->childRidMap.insert( node->collection.remoteId(), node );
146 void createRemoteNode(
const Collection &col )
149 kWarning() <<
"Collection '" << col.
name() <<
"' does not have a remote identifier - skipping";
152 RemoteNode *node =
new RemoteNode( col );
153 localRoot->pendingRemoteNodes.append( node );
160 createLocalNode( c );
164 void localCollectionFetchResult( KJob *job )
170 if ( !localPendingCollections.isEmpty() ) {
171 q->setError( Unknown );
172 q->setErrorText( i18n(
"Inconsistent local collection tree detected." ) );
177 localListDone =
true;
186 LocalNode* findLocalChildNodeByName( LocalNode *localParentNode,
const QString &name )
188 if ( name.isEmpty() )
191 if ( localParentNode == localRoot )
194 foreach ( LocalNode *childNode, localParentNode->childNodes ) {
196 if ( childNode->collection.name() == name && childNode->collection.remoteId().isEmpty() )
206 LocalNode* findMatchingLocalNode(
const Collection &collection )
208 if ( !hierarchicalRIDs ) {
209 if ( localRidMap.contains( collection.
remoteId() ) )
210 return localRidMap.value( collection.
remoteId() );
215 LocalNode *localParent = 0;
217 kWarning() <<
"Remote collection without valid parent found: " << collection;
221 localParent = localRoot;
226 if ( localParent->childRidMap.contains( collection.
remoteId() ) )
227 return localParent->childRidMap.value( collection.
remoteId() );
230 if ( LocalNode *recoveredLocalNode = findLocalChildNodeByName( localParent, collection.
name() ) ) {
231 kDebug() <<
"Recovering collection with lost RID:" << collection << recoveredLocalNode->collection;
232 return recoveredLocalNode;
244 LocalNode* findBestLocalAncestor(
const Collection &collection,
bool *exactMatch = 0 )
246 if ( !hierarchicalRIDs )
249 if ( exactMatch ) *exactMatch =
true;
253 kWarning() <<
"Remote collection without valid parent found: " << collection;
256 bool parentIsExact =
false;
257 LocalNode *localParent = findBestLocalAncestor( collection.
parentCollection(), &parentIsExact );
258 if ( !parentIsExact ) {
259 if ( exactMatch ) *exactMatch =
false;
262 if ( localParent->childRidMap.contains( collection.
remoteId() ) ) {
263 if ( exactMatch ) *exactMatch =
true;
264 return localParent->childRidMap.value( collection.
remoteId() );
266 if ( exactMatch ) *exactMatch =
false;
275 void processPendingRemoteNodes( LocalNode *_localRoot )
277 QList<RemoteNode*> pendingRemoteNodes( _localRoot->pendingRemoteNodes );
278 _localRoot->pendingRemoteNodes.clear();
279 QHash<LocalNode*, QList<RemoteNode*> > pendingCreations;
280 foreach ( RemoteNode *remoteNode, pendingRemoteNodes ) {
282 LocalNode *localNode = findMatchingLocalNode( remoteNode->collection );
284 Q_ASSERT( !localNode->processed );
285 updateLocalCollection( localNode, remoteNode );
289 localNode = findMatchingLocalNode( remoteNode->collection.parentCollection() );
291 pendingCreations[localNode].append( remoteNode );
295 localNode = findBestLocalAncestor( remoteNode->collection );
297 q->setError( Unknown );
298 q->setErrorText( i18n(
"Remote collection without root-terminated ancestor chain provided, resource is broken." ) );
302 localNode->pendingRemoteNodes.append( remoteNode );
306 for ( QHash<LocalNode*, QList<RemoteNode*> >::const_iterator it = pendingCreations.constBegin();
307 it != pendingCreations.constEnd(); ++it )
309 createLocalCollections( it.key(), it.value() );
316 void updateLocalCollection( LocalNode *localNode, RemoteNode *remoteNode )
319 Q_ASSERT( !upd.remoteId().isEmpty() );
320 upd.setId( localNode->collection.id() );
325 c.setParentCollection( localNode->collection.parentCollection() );
328 connect( mod, SIGNAL(result(KJob*)), q, SLOT(updateLocalCollectionResult(KJob*)) );
332 if ( !hierarchicalRIDs ) {
333 LocalNode *oldParent = localUidMap.value( localNode->collection.parentCollection().id() );
334 LocalNode *newParent = findMatchingLocalNode( remoteNode->collection.parentCollection() );
337 if ( newParent && oldParent != newParent ) {
340 connect( move, SIGNAL(result(KJob*)), q, SLOT(updateLocalCollectionResult(KJob*)) );
344 localNode->processed =
true;
348 void updateLocalCollectionResult( KJob* job )
353 if ( qobject_cast<CollectionModifyJob*>( job ) )
362 void createLocalCollections( LocalNode* localParent, QList<RemoteNode*> remoteNodes )
364 foreach ( RemoteNode *remoteNode, remoteNodes ) {
367 Q_ASSERT( !col.
remoteId().isEmpty() );
370 create->setProperty( LOCAL_NODE, QVariant::fromValue( localParent ) );
371 create->setProperty( REMOTE_NODE, QVariant::fromValue( remoteNode ) );
372 connect( create, SIGNAL(result(KJob*)), q, SLOT(createLocalCollectionResult(KJob*)) );
376 void createLocalCollectionResult( KJob* job )
383 LocalNode* localNode = createLocalNode( newLocal );
384 localNode->processed =
true;
386 LocalNode* localParent = job->property( LOCAL_NODE ).value<LocalNode*>();
387 Q_ASSERT( localParent->childNodes.contains( localNode ) );
388 RemoteNode* remoteNode = job->property( REMOTE_NODE ).value<RemoteNode*>();
392 processPendingRemoteNodes( localParent );
393 if ( !hierarchicalRIDs )
394 processPendingRemoteNodes( localRoot );
402 bool hasProcessedChildren( LocalNode *localNode )
const
404 if ( localNode->processed )
406 foreach ( LocalNode *child, localNode->childNodes ) {
407 if ( hasProcessedChildren( child ) )
417 Collection::List findUnprocessedLocalCollections( LocalNode *localNode )
const
420 if ( !localNode->processed ) {
421 if ( hasProcessedChildren( localNode ) ) {
422 kWarning() <<
"Found unprocessed local node with processed children, excluding from deletion";
423 kWarning() << localNode->collection;
426 if ( localNode->collection.remoteId().isEmpty() ) {
427 kWarning() <<
"Found unprocessed local node without remoteId, excluding from deletion";
428 kWarning() << localNode->collection;
431 rv.append( localNode->collection );
435 foreach ( LocalNode *child, localNode->childNodes )
436 rv.append( findUnprocessedLocalCollections( child ) );
443 void deleteUnprocessedLocalNodes()
448 deleteLocalCollections( cols );
457 q->setTotalAmount( KJob::Bytes, q->totalAmount( KJob::Bytes ) + cols.size() );
459 Q_ASSERT( !col.
remoteId().isEmpty() );
463 connect( job, SIGNAL(result(KJob*)), q, SLOT(deleteLocalCollectionsResult(KJob*)) );
469 q->setIgnoreJobFailure( job );
473 void deleteLocalCollectionsResult( KJob* )
486 kDebug() << Q_FUNC_INFO <<
"localListDone: " << localListDone <<
" deliveryDone: " << deliveryDone;
487 if ( !localListDone )
490 processPendingRemoteNodes( localRoot );
492 if ( !incremental && deliveryDone )
493 deleteUnprocessedLocalNodes();
495 if ( !hierarchicalRIDs ) {
496 deleteLocalCollections( removedRemoteCollections );
499 foreach (
const Collection &c, removedRemoteCollections ) {
500 LocalNode *node = findMatchingLocalNode( c );
502 localCols.append( node->collection );
504 deleteLocalCollections( localCols );
506 removedRemoteCollections.clear();
514 QList<RemoteNode*> findPendingRemoteNodes( LocalNode *localNode )
516 QList<RemoteNode*> rv;
517 rv.append( localNode->pendingRemoteNodes );
518 foreach ( LocalNode *child, localNode->childNodes )
519 rv.append( findPendingRemoteNodes( child ) );
529 q->setProcessedAmount( KJob::Bytes, progress );
532 if ( !deliveryDone || pendingJobs > 0 || !localListDone )
536 QList<RemoteNode*> orphans = findPendingRemoteNodes( localRoot );
537 if ( !orphans.isEmpty() ) {
538 q->setError( Unknown );
539 q->setErrorText( i18n(
"Found unresolved orphan collections" ) );
540 foreach ( RemoteNode* orphan, orphans )
541 kDebug() <<
"found orphan collection:" << orphan->collection;
546 kDebug() << Q_FUNC_INFO <<
"q->commit()";
557 LocalNode* localRoot;
558 QHash<Collection::Id, LocalNode*> localUidMap;
559 QHash<QString, LocalNode*> localRidMap;
562 QHash<Collection::Id, QVector<Collection::Id> > localPendingCollections;
569 bool hierarchicalRIDs;
577 d( new Private( this ) )
579 d->resourceId = resourceId;
580 setTotalAmount( KJob::Bytes, 0 );
590 setTotalAmount( KJob::Bytes, totalAmount( KJob::Bytes ) + remoteCollections.count() );
591 foreach (
const Collection &c, remoteCollections )
592 d->createRemoteNode( c );
595 d->deliveryDone =
true;
601 setTotalAmount( KJob::Bytes, totalAmount( KJob::Bytes ) + changedCollections.count() );
602 d->incremental =
true;
603 foreach (
const Collection &c, changedCollections )
604 d->createRemoteNode( c );
605 d->removedRemoteCollections += removedCollections;
608 d->deliveryDone =
true;
620 connect( job, SIGNAL(result(KJob*)), SLOT(localCollectionFetchResult(KJob*)) );
625 d->streaming = streaming;
630 d->deliveryDone =
true;
636 d->hierarchicalRIDs = hierarchical;
639 #include "collectionsync_p.moc"