Skip to content

Commit

Permalink
Move layer ref resolving to QgsMapLayerRef
Browse files Browse the repository at this point in the history
Add unit tests
  • Loading branch information
nyalldawson committed Apr 18, 2017
1 parent 35410fd commit 1efd623
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 15 deletions.
21 changes: 7 additions & 14 deletions src/core/layertree/qgslayertreelayer.cpp
Expand Up @@ -48,25 +48,18 @@ void QgsLayerTreeLayer::resolveReferences( const QgsProject *project, bool loose
if ( mRef.layer )
return; // already assigned

QgsMapLayer *layer = project->mapLayer( mRef.layerId );

if ( !layer && looseMatching && !mRef.name.isEmpty() )
if ( !looseMatching )
{
Q_FOREACH ( QgsMapLayer *l, project->mapLayersByName( mRef.name ) )
{
if ( mRef.layerMatchesSource( l ) )
{
layer = l;
break;
}
}
mRef.resolve( project );
}
else
{
mRef.resolveWeakly( project );
}

if ( !layer )
if ( !mRef.layer )
return;

mRef.layer = layer;
mRef.layerId = layer->id();
attachToLayer();
emit layerLoaded();
}
Expand Down
74 changes: 74 additions & 0 deletions src/core/qgsmaplayerref.h
Expand Up @@ -20,6 +20,7 @@

#include "qgsmaplayer.h"
#include "qgsdataprovider.h"
#include "qgsproject.h"

/** Internal structure to keep weak pointer to QgsMapLayer or layerId
* if the layer is not available yet.
Expand Down Expand Up @@ -54,6 +55,18 @@ struct _LayerRef
, provider( provider )
{}

/**
* Sets the reference to point to a specified layer.
*/
void setLayer( TYPE *l )
{
layer = l;
layerId = l ? l->id() : QString();
source = l ? l->publicSource() : QString();
name = l ? l->name() : QString();
provider = l && l->dataProvider() ? l->dataProvider()->name() : QString();
}

/**
* Returns a pointer to the layer, or nullptr if the reference has not yet been matched
* to a layer.
Expand All @@ -76,6 +89,7 @@ struct _LayerRef
/**
* Returns true if a layer matches the weak references to layer public source,
* layer name and data provider contained in this layer reference.
* \see resolveWeakly()
*/
bool layerMatchesSource( QgsMapLayer *layer ) const
{
Expand All @@ -88,6 +102,66 @@ struct _LayerRef

return true;
}

/**
* Resolves the map layer by attempting to find a layer with matching ID
* within a \a project. If found, this reference will be updated to match
* the found layer and the layer will be returned. If no matching layer is
* found, a nullptr is returned.
* \see resolveWeakly()
*/
TYPE *resolve( const QgsProject *project )
{
if ( project && !layerId.isEmpty() )
{
if ( TYPE *l = qobject_cast<TYPE *>( project->mapLayer( layerId ) ) )
{
setLayer( l );
return l;
}
}
return nullptr;
}

/**
* Resolves the map layer by attempting to find a matching layer
* in a \a project using a weak match.
*
* First, the layer is attempted to match to project layers using the
* layer's ID (calling this method implicitly calls resolve()).
*
* Failing a match by layer ID, the layer will be matched by using
* the weak references to layer public source, layer name and data
* provider contained in this layer reference.
*
* If a matching layer is found, this reference will be updated to match
* the found layer and the layer will be returned. If no matching layer is
* found, a nullptr is returned.
* \see resolve()
* \see layerMatchesSource()
*/
TYPE *resolveWeakly( const QgsProject *project )
{
// first try matching by layer ID
if ( resolve( project ) )
return layer;

if ( project && !name.isEmpty() )
{
Q_FOREACH ( QgsMapLayer *l, project->mapLayersByName( name ) )
{
if ( TYPE *tl = qobject_cast< TYPE *>( l ) )
{
if ( layerMatchesSource( tl ) )
{
setLayer( tl );
return tl;
}
}
}
}
return nullptr;
}
};

typedef _LayerRef<QgsMapLayer> QgsMapLayerRef;
Expand Down
41 changes: 40 additions & 1 deletion tests/src/core/testqgsmaplayer.cpp
Expand Up @@ -97,11 +97,12 @@ void TestQgsMapLayer::init()
QFileInfo myMapFileInfo( myFileName );
mpLayer = new QgsVectorLayer( myMapFileInfo.filePath(),
myMapFileInfo.completeBaseName(), QStringLiteral( "ogr" ) );
QgsProject::instance()->addMapLayer( mpLayer );
}

void TestQgsMapLayer::cleanup()
{
delete mpLayer;
QgsProject::instance()->removeAllMapLayers();
}

void TestQgsMapLayer::cleanupTestCase()
Expand Down Expand Up @@ -180,6 +181,44 @@ void TestQgsMapLayer::layerRef()

// verify that weak reference matches layer
QVERIFY( ref2.layerMatchesSource( mpLayer ) );

// resolve layer using project
QCOMPARE( ref2.resolve( QgsProject::instance() ), mpLayer );
QCOMPARE( ref2.get(), mpLayer );
QCOMPARE( ref2.layer.data(), mpLayer );
QCOMPARE( ref2.layerId, mpLayer->id() );
QCOMPARE( ref2.name, QStringLiteral( "points" ) );
QCOMPARE( ref2.source, mpLayer->publicSource() );
QCOMPARE( ref2.provider, QStringLiteral( "ogr" ) );

// setLayer
QgsVectorLayerRef ref3;
QVERIFY( !ref3.get() );
ref3.setLayer( mpLayer );
QCOMPARE( ref3.get(), mpLayer );
QCOMPARE( ref3.layer.data(), mpLayer );
QCOMPARE( ref3.layerId, mpLayer->id() );
QCOMPARE( ref3.name, QStringLiteral( "points" ) );
QCOMPARE( ref3.source, mpLayer->publicSource() );
QCOMPARE( ref3.provider, QStringLiteral( "ogr" ) );

// weak resolve
QgsVectorLayerRef ref4( QStringLiteral( "badid" ), QStringLiteral( "points" ), mpLayer->publicSource(), QStringLiteral( "ogr" ) );
QVERIFY( !ref4.get() );
QVERIFY( !ref4.resolve( QgsProject::instance() ) );
QCOMPARE( ref4.resolveWeakly( QgsProject::instance() ), mpLayer );
QCOMPARE( ref4.get(), mpLayer );
QCOMPARE( ref4.layer.data(), mpLayer );
QCOMPARE( ref4.layerId, mpLayer->id() );
QCOMPARE( ref4.name, QStringLiteral( "points" ) );
QCOMPARE( ref4.source, mpLayer->publicSource() );
QCOMPARE( ref4.provider, QStringLiteral( "ogr" ) );

// try resolving a bad reference
QgsVectorLayerRef ref5( QStringLiteral( "badid" ), QStringLiteral( "points" ), mpLayer->publicSource(), QStringLiteral( "xxx" ) );
QVERIFY( !ref5.get() );
QVERIFY( !ref5.resolve( QgsProject::instance() ) );
QVERIFY( !ref5.resolveWeakly( QgsProject::instance() ) );
}

QGSTEST_MAIN( TestQgsMapLayer )
Expand Down

0 comments on commit 1efd623

Please sign in to comment.