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"
29 #include "entitydisplayattribute.h"
33 #include <QtCore/QVariant>
35 using namespace Akonadi;
51 qDeleteAll( childNodes );
52 qDeleteAll( pendingRemoteNodes );
56 QList<LocalNode*> childNodes;
57 QHash<QString, LocalNode*> childRidMap;
61 QList<RemoteNode*> pendingRemoteNodes;
65 Q_DECLARE_METATYPE( LocalNode* )
66 static const
char LOCAL_NODE[] = "LocalNode";
81 Q_DECLARE_METATYPE( RemoteNode* )
82 static const
char REMOTE_NODE[] = "RemoteNode";
96 hierarchicalRIDs( false ),
97 localListDone( false ),
101 localRoot->processed =
true;
102 localUidMap.insert( localRoot->collection.id(), localRoot );
103 if ( !hierarchicalRIDs ) {
104 localRidMap.insert( QString(), localRoot );
114 LocalNode* createLocalNode(
const Collection &col )
116 LocalNode *node =
new LocalNode( col );
117 Q_ASSERT( !localUidMap.contains( col.
id() ) );
118 localUidMap.insert( node->collection.id(), node );
119 if ( !hierarchicalRIDs && !col.
remoteId().isEmpty() ) {
120 localRidMap.insert( node->collection.remoteId(), node );
124 if ( localPendingCollections.contains( col.
id() ) ) {
125 QVector<Collection::Id> childIds = localPendingCollections.take( col.
id() );
127 Q_ASSERT( localUidMap.contains( childId ) );
128 LocalNode *childNode = localUidMap.value( childId );
129 node->childNodes.append( childNode );
130 if ( !childNode->collection.remoteId().isEmpty() ) {
131 node->childRidMap.insert( childNode->collection.remoteId(), childNode );
139 parentNode->childNodes.append( node );
140 if ( !node->collection.remoteId().isEmpty() ) {
141 parentNode->childRidMap.insert( node->collection.remoteId(), node );
151 void createRemoteNode(
const Collection &col )
154 kWarning() <<
"Collection '" << col.
name() <<
"' does not have a remote identifier - skipping";
157 RemoteNode *node =
new RemoteNode( col );
158 localRoot->pendingRemoteNodes.append( node );
165 createLocalNode( c );
169 void localCollectionFetchResult( KJob *job )
171 if ( job->error() ) {
176 if ( !localPendingCollections.isEmpty() ) {
177 q->setError( Unknown );
178 q->setErrorText( i18n(
"Inconsistent local collection tree detected." ) );
183 localListDone =
true;
192 LocalNode* findLocalChildNodeByName( LocalNode *localParentNode,
const QString &name )
194 if ( name.isEmpty() ) {
198 if ( localParentNode == localRoot ) {
202 foreach ( LocalNode *childNode, localParentNode->childNodes ) {
204 if ( childNode->collection.name() == name && childNode->collection.remoteId().isEmpty() ) {
215 LocalNode* findMatchingLocalNode(
const Collection &collection )
217 if ( !hierarchicalRIDs ) {
218 if ( localRidMap.contains( collection.
remoteId() ) ) {
219 return localRidMap.value( collection.
remoteId() );
226 LocalNode *localParent = 0;
228 kWarning() <<
"Remote collection without valid parent found: " << collection;
232 localParent = localRoot;
238 if ( localParent->childRidMap.contains( collection.
remoteId() ) ) {
239 return localParent->childRidMap.value( collection.
remoteId() );
243 if ( LocalNode *recoveredLocalNode = findLocalChildNodeByName( localParent, collection.
name() ) ) {
244 kDebug() <<
"Recovering collection with lost RID:" << collection << recoveredLocalNode->collection;
245 return recoveredLocalNode;
257 LocalNode* findBestLocalAncestor(
const Collection &collection,
bool *exactMatch = 0 )
259 if ( !hierarchicalRIDs ) {
269 kWarning() <<
"Remote collection without valid parent found: " << collection;
272 bool parentIsExact =
false;
273 LocalNode *localParent = findBestLocalAncestor( collection.
parentCollection(), &parentIsExact );
274 if ( !parentIsExact ) {
280 if ( localParent->childRidMap.contains( collection.
remoteId() ) ) {
284 return localParent->childRidMap.value( collection.
remoteId() );
297 void processPendingRemoteNodes( LocalNode *_localRoot )
299 QList<RemoteNode*> pendingRemoteNodes( _localRoot->pendingRemoteNodes );
300 _localRoot->pendingRemoteNodes.clear();
301 QHash<LocalNode*, QList<RemoteNode*> > pendingCreations;
302 foreach ( RemoteNode *remoteNode, pendingRemoteNodes ) {
304 LocalNode *localNode = findMatchingLocalNode( remoteNode->collection );
306 Q_ASSERT( !localNode->processed );
307 updateLocalCollection( localNode, remoteNode );
311 localNode = findMatchingLocalNode( remoteNode->collection.parentCollection() );
313 pendingCreations[localNode].append( remoteNode );
317 localNode = findBestLocalAncestor( remoteNode->collection );
319 q->setError( Unknown );
320 q->setErrorText( i18n(
"Remote collection without root-terminated ancestor chain provided, resource is broken." ) );
324 localNode->pendingRemoteNodes.append( remoteNode );
328 for ( QHash<LocalNode*, QList<RemoteNode*> >::const_iterator it = pendingCreations.constBegin();
329 it != pendingCreations.constEnd(); ++it ) {
330 createLocalCollections( it.key(), it.value() );
337 void updateLocalCollection( LocalNode *localNode, RemoteNode *remoteNode )
340 Q_ASSERT( !upd.remoteId().isEmpty() );
341 upd.setId( localNode->collection.id() );
351 c.setParentCollection( localNode->collection.parentCollection() );
354 connect( mod, SIGNAL(result(KJob*)), q, SLOT(updateLocalCollectionResult(KJob*)) );
358 if ( !hierarchicalRIDs ) {
359 LocalNode *oldParent = localUidMap.value( localNode->collection.parentCollection().id() );
360 LocalNode *newParent = findMatchingLocalNode( remoteNode->collection.parentCollection() );
363 if ( newParent && oldParent != newParent ) {
366 connect( move, SIGNAL(result(KJob*)), q, SLOT(updateLocalCollectionResult(KJob*)) );
370 localNode->processed =
true;
374 void updateLocalCollectionResult( KJob* job )
377 if ( job->error() ) {
380 if ( qobject_cast<CollectionModifyJob*>( job ) ) {
390 void createLocalCollections( LocalNode* localParent, QList<RemoteNode*> remoteNodes )
392 foreach ( RemoteNode *remoteNode, remoteNodes ) {
395 Q_ASSERT( !col.
remoteId().isEmpty() );
398 create->setProperty( LOCAL_NODE, QVariant::fromValue( localParent ) );
399 create->setProperty( REMOTE_NODE, QVariant::fromValue( remoteNode ) );
400 connect( create, SIGNAL(result(KJob*)), q, SLOT(createLocalCollectionResult(KJob*)) );
404 void createLocalCollectionResult( KJob* job )
407 if ( job->error() ) {
412 LocalNode *localNode = createLocalNode( newLocal );
413 localNode->processed =
true;
415 LocalNode *localParent = job->property( LOCAL_NODE ).value<LocalNode*>();
416 Q_ASSERT( localParent->childNodes.contains( localNode ) );
417 RemoteNode *remoteNode = job->property( REMOTE_NODE ).value<RemoteNode*>();
421 processPendingRemoteNodes( localParent );
422 if ( !hierarchicalRIDs ) {
423 processPendingRemoteNodes( localRoot );
432 bool hasProcessedChildren( LocalNode *localNode )
const
434 if ( localNode->processed ) {
437 foreach ( LocalNode *child, localNode->childNodes ) {
438 if ( hasProcessedChildren( child ) ) {
449 Collection::List findUnprocessedLocalCollections( LocalNode *localNode )
const
452 if ( !localNode->processed ) {
453 if ( hasProcessedChildren( localNode ) ) {
454 kWarning() <<
"Found unprocessed local node with processed children, excluding from deletion";
455 kWarning() << localNode->collection;
458 if ( localNode->collection.remoteId().isEmpty() ) {
459 kWarning() <<
"Found unprocessed local node without remoteId, excluding from deletion";
460 kWarning() << localNode->collection;
463 rv.append( localNode->collection );
467 foreach ( LocalNode *child, localNode->childNodes ) {
468 rv.append( findUnprocessedLocalCollections( child ) );
476 void deleteUnprocessedLocalNodes()
482 deleteLocalCollections( cols );
491 q->setTotalAmount( KJob::Bytes, q->totalAmount( KJob::Bytes ) + cols.size() );
493 Q_ASSERT( !col.
remoteId().isEmpty() );
497 connect( job, SIGNAL(result(KJob*)), q, SLOT(deleteLocalCollectionsResult(KJob*)) );
503 q->setIgnoreJobFailure( job );
507 void deleteLocalCollectionsResult( KJob* )
520 kDebug() << Q_FUNC_INFO <<
"localListDone: " << localListDone <<
" deliveryDone: " << deliveryDone;
521 if ( !localListDone ) {
525 processPendingRemoteNodes( localRoot );
527 if ( !incremental && deliveryDone ) {
528 deleteUnprocessedLocalNodes();
531 if ( !hierarchicalRIDs ) {
532 deleteLocalCollections( removedRemoteCollections );
535 foreach (
const Collection &c, removedRemoteCollections ) {
536 LocalNode *node = findMatchingLocalNode( c );
538 localCols.append( node->collection );
541 deleteLocalCollections( localCols );
543 removedRemoteCollections.clear();
551 QList<RemoteNode*> findPendingRemoteNodes( LocalNode *localNode )
553 QList<RemoteNode*> rv;
554 rv.append( localNode->pendingRemoteNodes );
555 foreach ( LocalNode *child, localNode->childNodes ) {
556 rv.append( findPendingRemoteNodes( child ) );
567 q->setProcessedAmount( KJob::Bytes, progress );
570 if ( !deliveryDone || pendingJobs > 0 || !localListDone ) {
575 QList<RemoteNode*> orphans = findPendingRemoteNodes( localRoot );
576 if ( !orphans.isEmpty() ) {
577 q->setError( Unknown );
578 q->setErrorText( i18n(
"Found unresolved orphan collections" ) );
579 foreach ( RemoteNode* orphan, orphans ) {
580 kDebug() <<
"found orphan collection:" << orphan->collection;
586 kDebug() << Q_FUNC_INFO <<
"q->commit()";
597 LocalNode* localRoot;
598 QHash<Collection::Id, LocalNode*> localUidMap;
599 QHash<QString, LocalNode*> localRidMap;
602 QHash<Collection::Id, QVector<Collection::Id> > localPendingCollections;
609 bool hierarchicalRIDs;
617 d( new Private( this ) )
619 d->resourceId = resourceId;
620 setTotalAmount( KJob::Bytes, 0 );
630 setTotalAmount( KJob::Bytes, totalAmount( KJob::Bytes ) + remoteCollections.count() );
631 foreach (
const Collection &c, remoteCollections ) {
632 d->createRemoteNode( c );
635 if ( !d->streaming ) {
636 d->deliveryDone =
true;
643 setTotalAmount( KJob::Bytes, totalAmount( KJob::Bytes ) + changedCollections.count() );
644 d->incremental =
true;
645 foreach (
const Collection &c, changedCollections ) {
646 d->createRemoteNode( c );
648 d->removedRemoteCollections += removedCollections;
650 if ( !d->streaming ) {
651 d->deliveryDone =
true;
664 connect( job, SIGNAL(result(KJob*)), SLOT(localCollectionFetchResult(KJob*)) );
669 d->streaming = streaming;
674 d->deliveryDone =
true;
680 d->hierarchicalRIDs = hierarchical;
683 #include "moc_collectionsync_p.cpp"