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 );
113 LocalNode* createLocalNode(
const Collection &col )
115 LocalNode *node =
new LocalNode( col );
116 Q_ASSERT( !localUidMap.contains( col.
id() ) );
117 localUidMap.insert( node->collection.id(), node );
118 if ( !hierarchicalRIDs && !col.
remoteId().isEmpty() ) {
119 localRidMap.insert( node->collection.remoteId(), node );
123 if ( localPendingCollections.contains( col.
id() ) ) {
124 QVector<Collection::Id> childIds = localPendingCollections.take( col.
id() );
126 Q_ASSERT( localUidMap.contains( childId ) );
127 LocalNode *childNode = localUidMap.value( childId );
128 node->childNodes.append( childNode );
129 if ( !childNode->collection.remoteId().isEmpty() ) {
130 node->childRidMap.insert( childNode->collection.remoteId(), childNode );
138 parentNode->childNodes.append( node );
139 if ( !node->collection.remoteId().isEmpty() ) {
140 parentNode->childRidMap.insert( node->collection.remoteId(), node );
150 void createRemoteNode(
const Collection &col )
153 kWarning() <<
"Collection '" << col.
name() <<
"' does not have a remote identifier - skipping";
156 RemoteNode *node =
new RemoteNode( col );
157 localRoot->pendingRemoteNodes.append( node );
164 createLocalNode( c );
168 void localCollectionFetchResult( KJob *job )
170 if ( job->error() ) {
175 if ( !localPendingCollections.isEmpty() ) {
176 q->setError( Unknown );
177 q->setErrorText( i18n(
"Inconsistent local collection tree detected." ) );
182 localListDone =
true;
191 LocalNode* findLocalChildNodeByName( LocalNode *localParentNode,
const QString &name )
193 if ( name.isEmpty() ) {
197 if ( localParentNode == localRoot ) {
201 foreach ( LocalNode *childNode, localParentNode->childNodes ) {
203 if ( childNode->collection.name() == name && childNode->collection.remoteId().isEmpty() ) {
214 LocalNode* findMatchingLocalNode(
const Collection &collection )
216 if ( !hierarchicalRIDs ) {
217 if ( localRidMap.contains( collection.
remoteId() ) ) {
218 return localRidMap.value( collection.
remoteId() );
225 LocalNode *localParent = 0;
227 kWarning() <<
"Remote collection without valid parent found: " << collection;
231 localParent = localRoot;
237 if ( localParent->childRidMap.contains( collection.
remoteId() ) ) {
238 return localParent->childRidMap.value( collection.
remoteId() );
242 if ( LocalNode *recoveredLocalNode = findLocalChildNodeByName( localParent, collection.
name() ) ) {
243 kDebug() <<
"Recovering collection with lost RID:" << collection << recoveredLocalNode->collection;
244 return recoveredLocalNode;
256 LocalNode* findBestLocalAncestor(
const Collection &collection,
bool *exactMatch = 0 )
258 if ( !hierarchicalRIDs ) {
268 kWarning() <<
"Remote collection without valid parent found: " << collection;
271 bool parentIsExact =
false;
272 LocalNode *localParent = findBestLocalAncestor( collection.
parentCollection(), &parentIsExact );
273 if ( !parentIsExact ) {
279 if ( localParent->childRidMap.contains( collection.
remoteId() ) ) {
283 return localParent->childRidMap.value( collection.
remoteId() );
296 void processPendingRemoteNodes( LocalNode *_localRoot )
298 QList<RemoteNode*> pendingRemoteNodes( _localRoot->pendingRemoteNodes );
299 _localRoot->pendingRemoteNodes.clear();
300 QHash<LocalNode*, QList<RemoteNode*> > pendingCreations;
301 foreach ( RemoteNode *remoteNode, pendingRemoteNodes ) {
303 LocalNode *localNode = findMatchingLocalNode( remoteNode->collection );
305 Q_ASSERT( !localNode->processed );
306 updateLocalCollection( localNode, remoteNode );
310 localNode = findMatchingLocalNode( remoteNode->collection.parentCollection() );
312 pendingCreations[localNode].append( remoteNode );
316 localNode = findBestLocalAncestor( remoteNode->collection );
318 q->setError( Unknown );
319 q->setErrorText( i18n(
"Remote collection without root-terminated ancestor chain provided, resource is broken." ) );
323 localNode->pendingRemoteNodes.append( remoteNode );
327 for ( QHash<LocalNode*, QList<RemoteNode*> >::const_iterator it = pendingCreations.constBegin();
328 it != pendingCreations.constEnd(); ++it ) {
329 createLocalCollections( it.key(), it.value() );
336 void updateLocalCollection( LocalNode *localNode, RemoteNode *remoteNode )
339 Q_ASSERT( !upd.remoteId().isEmpty() );
340 upd.setId( localNode->collection.id() );
345 c.setParentCollection( localNode->collection.parentCollection() );
348 connect( mod, SIGNAL(result(KJob*)), q, SLOT(updateLocalCollectionResult(KJob*)) );
352 if ( !hierarchicalRIDs ) {
353 LocalNode *oldParent = localUidMap.value( localNode->collection.parentCollection().id() );
354 LocalNode *newParent = findMatchingLocalNode( remoteNode->collection.parentCollection() );
357 if ( newParent && oldParent != newParent ) {
360 connect( move, SIGNAL(result(KJob*)), q, SLOT(updateLocalCollectionResult(KJob*)) );
364 localNode->processed =
true;
368 void updateLocalCollectionResult( KJob* job )
371 if ( job->error() ) {
374 if ( qobject_cast<CollectionModifyJob*>( job ) ) {
384 void createLocalCollections( LocalNode* localParent, QList<RemoteNode*> remoteNodes )
386 foreach ( RemoteNode *remoteNode, remoteNodes ) {
389 Q_ASSERT( !col.
remoteId().isEmpty() );
392 create->setProperty( LOCAL_NODE, QVariant::fromValue( localParent ) );
393 create->setProperty( REMOTE_NODE, QVariant::fromValue( remoteNode ) );
394 connect( create, SIGNAL(result(KJob*)), q, SLOT(createLocalCollectionResult(KJob*)) );
398 void createLocalCollectionResult( KJob* job )
401 if ( job->error() ) {
406 LocalNode *localNode = createLocalNode( newLocal );
407 localNode->processed =
true;
409 LocalNode *localParent = job->property( LOCAL_NODE ).value<LocalNode*>();
410 Q_ASSERT( localParent->childNodes.contains( localNode ) );
411 RemoteNode *remoteNode = job->property( REMOTE_NODE ).value<RemoteNode*>();
415 processPendingRemoteNodes( localParent );
416 if ( !hierarchicalRIDs ) {
417 processPendingRemoteNodes( localRoot );
426 bool hasProcessedChildren( LocalNode *localNode )
const
428 if ( localNode->processed ) {
431 foreach ( LocalNode *child, localNode->childNodes ) {
432 if ( hasProcessedChildren( child ) ) {
443 Collection::List findUnprocessedLocalCollections( LocalNode *localNode )
const
446 if ( !localNode->processed ) {
447 if ( hasProcessedChildren( localNode ) ) {
448 kWarning() <<
"Found unprocessed local node with processed children, excluding from deletion";
449 kWarning() << localNode->collection;
452 if ( localNode->collection.remoteId().isEmpty() ) {
453 kWarning() <<
"Found unprocessed local node without remoteId, excluding from deletion";
454 kWarning() << localNode->collection;
457 rv.append( localNode->collection );
461 foreach ( LocalNode *child, localNode->childNodes ) {
462 rv.append( findUnprocessedLocalCollections( child ) );
470 void deleteUnprocessedLocalNodes()
476 deleteLocalCollections( cols );
485 q->setTotalAmount( KJob::Bytes, q->totalAmount( KJob::Bytes ) + cols.size() );
487 Q_ASSERT( !col.
remoteId().isEmpty() );
491 connect( job, SIGNAL(result(KJob*)), q, SLOT(deleteLocalCollectionsResult(KJob*)) );
497 q->setIgnoreJobFailure( job );
501 void deleteLocalCollectionsResult( KJob* )
514 kDebug() << Q_FUNC_INFO <<
"localListDone: " << localListDone <<
" deliveryDone: " << deliveryDone;
515 if ( !localListDone ) {
519 processPendingRemoteNodes( localRoot );
521 if ( !incremental && deliveryDone ) {
522 deleteUnprocessedLocalNodes();
525 if ( !hierarchicalRIDs ) {
526 deleteLocalCollections( removedRemoteCollections );
529 foreach (
const Collection &c, removedRemoteCollections ) {
530 LocalNode *node = findMatchingLocalNode( c );
532 localCols.append( node->collection );
535 deleteLocalCollections( localCols );
537 removedRemoteCollections.clear();
545 QList<RemoteNode*> findPendingRemoteNodes( LocalNode *localNode )
547 QList<RemoteNode*> rv;
548 rv.append( localNode->pendingRemoteNodes );
549 foreach ( LocalNode *child, localNode->childNodes ) {
550 rv.append( findPendingRemoteNodes( child ) );
561 q->setProcessedAmount( KJob::Bytes, progress );
564 if ( !deliveryDone || pendingJobs > 0 || !localListDone ) {
569 QList<RemoteNode*> orphans = findPendingRemoteNodes( localRoot );
570 if ( !orphans.isEmpty() ) {
571 q->setError( Unknown );
572 q->setErrorText( i18n(
"Found unresolved orphan collections" ) );
573 foreach ( RemoteNode* orphan, orphans ) {
574 kDebug() <<
"found orphan collection:" << orphan->collection;
580 kDebug() << Q_FUNC_INFO <<
"q->commit()";
591 LocalNode* localRoot;
592 QHash<Collection::Id, LocalNode*> localUidMap;
593 QHash<QString, LocalNode*> localRidMap;
596 QHash<Collection::Id, QVector<Collection::Id> > localPendingCollections;
603 bool hierarchicalRIDs;
611 d( new Private( this ) )
613 d->resourceId = resourceId;
614 setTotalAmount( KJob::Bytes, 0 );
624 setTotalAmount( KJob::Bytes, totalAmount( KJob::Bytes ) + remoteCollections.count() );
625 foreach (
const Collection &c, remoteCollections ) {
626 d->createRemoteNode( c );
629 if ( !d->streaming ) {
630 d->deliveryDone =
true;
637 setTotalAmount( KJob::Bytes, totalAmount( KJob::Bytes ) + changedCollections.count() );
638 d->incremental =
true;
639 foreach (
const Collection &c, changedCollections ) {
640 d->createRemoteNode( c );
642 d->removedRemoteCollections += removedCollections;
644 if ( !d->streaming ) {
645 d->deliveryDone =
true;
658 connect( job, SIGNAL(result(KJob*)), SLOT(localCollectionFetchResult(KJob*)) );
663 d->streaming = streaming;
668 d->deliveryDone =
true;
674 d->hierarchicalRIDs = hierarchical;
677 #include "moc_collectionsync_p.cpp"