Implicit sharing for QgsDataDefined

This commit is contained in:
Nyall Dawson 2015-05-08 16:25:39 +10:00
parent d970daafa3
commit 14fd976f06
6 changed files with 256 additions and 106 deletions

View File

@ -1,6 +1,7 @@
/** \ingroup core
* \class QgsDataDefined
* A container class for data source field mapping or expression.
* \note QgsDataDefined objects are implicitly shared.
*/
class QgsDataDefined
@ -115,7 +116,7 @@ class QgsDataDefined
* to differentiate multiple QgsDataDefineds encoded in the same string map.
* @see fromMap
*/
QMap< QString, QString > toMap( const QString& baseName = QString() );
QMap< QString, QString > toMap( const QString& baseName = QString() ) const;
/**Returns a DOM element containing the properties of the data defined container.
* @param document DOM document

View File

@ -487,6 +487,7 @@ SET(QGIS_CORE_HDRS
qgscsexception.h
qgsdartmeasurement.h
qgsdatadefined.h
qgsdatadefined_p.h
qgsdataitem.h
qgsdataitemprovider.h
qgsdataitemproviderregistry.h

View File

@ -14,6 +14,7 @@
***************************************************************************/
#include "qgsdatadefined.h"
#include "qgsdatadefined_p.h"
#include "qgslogger.h"
#include "qgsexpression.h"
@ -24,33 +25,22 @@ QgsDataDefined::QgsDataDefined( bool active,
bool useexpr,
const QString& expr,
const QString& field )
: mActive( active )
, mUseExpression( useexpr )
, mExpressionString( expr )
, mField( field )
{
mExpression = 0;
mExpressionPrepared = false;
d = new QgsDataDefinedPrivate( active, useexpr, expr, field );
}
QgsDataDefined::QgsDataDefined( const QgsExpression * expression )
: mActive( bool( expression ) )
, mUseExpression( expression && ! expression->isField() )
, mExpressionString( mUseExpression ? expression->expression() : "" )
, mField( !mUseExpression ? ( expression ? expression->expression() : "" ) : "" )
{
mExpression = 0;
mExpressionPrepared = false;
bool active = bool( expression );
bool useExpression = expression && ! expression->isField();
d = new QgsDataDefinedPrivate( active,
useExpression,
useExpression ? expression->expression() : QString(),
!useExpression ? ( expression ? expression->expression() : QString() ) : QString() );
}
QgsDataDefined::QgsDataDefined( const QgsDataDefined &other )
: mExpression( 0 )
, mActive( other.isActive() )
, mUseExpression( other.useExpression() )
, mExpressionString( other.expressionString() )
, mField( other.field() )
, mExpressionParams( other.expressionParams() )
, mExpressionPrepared( false )
: d( other.d )
{
}
@ -80,35 +70,80 @@ QgsDataDefined* QgsDataDefined::fromMap( const QgsStringMap &map, const QString
QgsDataDefined::QgsDataDefined( const QString & string )
{
QgsExpression expression( string );
mActive = expression.rootNode();
mUseExpression = mActive && ! expression.isField();
mExpressionString = mUseExpression ? expression.expression() : QString();
mField = expression.isField() ? expression.rootNode()->dump() : QString();
mExpression = 0;
mExpressionPrepared = false;
bool active = expression.rootNode();
bool useExpression = active && ! expression.isField();
d = new QgsDataDefinedPrivate( active,
useExpression,
useExpression ? expression.expression() : QString(),
expression.isField() ? expression.rootNode()->dump() : QString() );
}
QgsDataDefined::~QgsDataDefined()
{
delete mExpression;
}
bool QgsDataDefined::hasDefaultValues() const
{
return ( !mActive && !mUseExpression && mExpressionString.isEmpty() && mField.isEmpty() );
return ( !d->active && !d->useExpression && d->expressionString.isEmpty() && d->field.isEmpty() );
}
bool QgsDataDefined::isActive() const
{
return d->active;
}
void QgsDataDefined::setActive( bool active )
{
if ( active == d->active )
return;
d.detach();
d->active = active;
}
bool QgsDataDefined::useExpression() const
{
return d->useExpression;
}
void QgsDataDefined::setUseExpression( bool use )
{
mUseExpression = use;
mExprRefColumns.clear();
if ( use == d->useExpression )
return;
d.detach();
d->useExpression = use;
d->exprRefColumns.clear();
}
QString QgsDataDefined::expressionString() const
{
return d->expressionString;
}
void QgsDataDefined::setExpressionString( const QString &expr )
{
mExpressionString = expr;
mExpressionPrepared = false;
mExprRefColumns.clear();
if ( expr == d->expressionString )
return;
d.detach();
d->expressionString = expr;
d->expressionPrepared = false;
d->exprRefColumns.clear();
}
QMap<QString, QVariant> QgsDataDefined::expressionParams() const
{
return d->expressionParams;
}
void QgsDataDefined::setExpressionParams( QMap<QString, QVariant> params )
{
d.detach();
d->expressionParams = params;
}
bool QgsDataDefined::prepareExpression( QgsVectorLayer* layer )
@ -127,44 +162,58 @@ bool QgsDataDefined::prepareExpression( QgsVectorLayer* layer )
bool QgsDataDefined::prepareExpression( const QgsFields &fields )
{
if ( !mUseExpression || mExpressionString.isEmpty() )
if ( !d->useExpression || d->expressionString.isEmpty() )
{
return false;
}
mExpression = new QgsExpression( mExpressionString );
if ( mExpression->hasParserError() )
d.detach();
delete d->expression;
d->expression = new QgsExpression( d->expressionString );
if ( d->expression->hasParserError() )
{
QgsDebugMsg( "Parser error:" + mExpression->parserErrorString() );
QgsDebugMsg( "Parser error:" + d->expression->parserErrorString() );
return false;
}
// setup expression parameters
QVariant scaleV = mExpressionParams.value( "scale" );
QVariant scaleV = d->expressionParams.value( "scale" );
if ( scaleV.isValid() )
{
bool ok;
double scale = scaleV.toDouble( &ok );
if ( ok )
{
mExpression->setScale( scale );
d->expression->setScale( scale );
}
}
mExpression->prepare( fields );
mExprRefColumns = mExpression->referencedColumns();
d->expression->prepare( fields );
d->exprRefColumns = d->expression->referencedColumns();
if ( mExpression->hasEvalError() )
if ( d->expression->hasEvalError() )
{
QgsDebugMsg( "Prepare error:" + mExpression->evalErrorString() );
d->expressionPrepared = false;
QgsDebugMsg( "Prepare error:" + d->expression->evalErrorString() );
return false;
}
mExpressionPrepared = true;
d->expressionPrepared = true;
return true;
}
bool QgsDataDefined::expressionIsPrepared() const
{
return d->expressionPrepared;
}
QgsExpression *QgsDataDefined::expression()
{
d.detach();
return d->expression;
}
QStringList QgsDataDefined::referencedColumns( QgsVectorLayer* layer )
{
if ( layer )
@ -179,38 +228,49 @@ QStringList QgsDataDefined::referencedColumns( QgsVectorLayer* layer )
QStringList QgsDataDefined::referencedColumns( const QgsFields &fields )
{
if ( !mExprRefColumns.isEmpty() )
if ( !d->exprRefColumns.isEmpty() )
{
return mExprRefColumns;
return d->exprRefColumns;
}
if ( mUseExpression )
d.detach();
if ( d->useExpression )
{
if ( !mExpression || !mExpressionPrepared )
if ( !d->expression || !d->expressionPrepared )
{
prepareExpression( fields );
}
}
else if ( !mField.isEmpty() )
else if ( !d->field.isEmpty() )
{
mExprRefColumns << mField;
d->exprRefColumns << d->field;
}
return mExprRefColumns;
return d->exprRefColumns;
}
QString QgsDataDefined::field() const
{
return d->field;
}
void QgsDataDefined::setField( const QString &field )
{
mField = field;
mExprRefColumns.clear();
if ( field == d->field )
return;
d.detach();
d->field = field;
d->exprRefColumns.clear();
}
void QgsDataDefined::insertExpressionParam( QString key, QVariant param )
{
mExpressionParams.insert( key, param );
d.detach();
d->expressionParams.insert( key, param );
}
QgsStringMap QgsDataDefined::toMap( const QString &baseName )
QgsStringMap QgsDataDefined::toMap( const QString &baseName ) const
{
QgsStringMap map;
QString prefix;
@ -219,10 +279,10 @@ QgsStringMap QgsDataDefined::toMap( const QString &baseName )
prefix.append( QString( "%1_dd_" ).arg( baseName ) );
}
map.insert( QString( "%1active" ).arg( prefix ), ( mActive ? "1" : "0" ) );
map.insert( QString( "%1useexpr" ).arg( prefix ), ( mUseExpression ? "1" : "0" ) );
map.insert( QString( "%1expression" ).arg( prefix ), mExpressionString );
map.insert( QString( "%1field" ).arg( prefix ), mField );
map.insert( QString( "%1active" ).arg( prefix ), ( d->active ? "1" : "0" ) );
map.insert( QString( "%1useexpr" ).arg( prefix ), ( d->useExpression ? "1" : "0" ) );
map.insert( QString( "%1expression" ).arg( prefix ), d->expressionString );
map.insert( QString( "%1field" ).arg( prefix ), d->field );
return map;
}
@ -230,10 +290,10 @@ QgsStringMap QgsDataDefined::toMap( const QString &baseName )
QDomElement QgsDataDefined::toXmlElement( QDomDocument &document, const QString& elementName ) const
{
QDomElement element = document.createElement( elementName );
element.setAttribute( "active", mActive ? "true" : "false" );
element.setAttribute( "useExpr", mUseExpression ? "true" : "false" );
element.setAttribute( "expr", mExpressionString );
element.setAttribute( "field", mField );
element.setAttribute( "active", d->active ? "true" : "false" );
element.setAttribute( "useExpr", d->useExpression ? "true" : "false" );
element.setAttribute( "expr", d->expressionString );
element.setAttribute( "field", d->field );
return element;
}
@ -244,17 +304,17 @@ bool QgsDataDefined::setFromXmlElement( const QDomElement &element )
return false;
}
mActive = element.attribute( "active" ).compare( "true", Qt::CaseInsensitive ) == 0;
mUseExpression = element.attribute( "useExpr" ).compare( "true", Qt::CaseInsensitive ) == 0;
mField = element.attribute( "field" );
d.detach();
d->active = element.attribute( "active" ).compare( "true", Qt::CaseInsensitive ) == 0;
d->useExpression = element.attribute( "useExpr" ).compare( "true", Qt::CaseInsensitive ) == 0;
d->field = element.attribute( "field" );
setExpressionString( element.attribute( "expr" ) );
return true;
}
bool QgsDataDefined::operator==( const QgsDataDefined &other ) const
{
return other.isActive() == mActive && other.useExpression() == mUseExpression &&
other.field() == mField && other.expressionString() == mExpressionString;
return *( other.d ) == *d;
}
bool QgsDataDefined::operator!=( const QgsDataDefined &other ) const
@ -264,17 +324,7 @@ bool QgsDataDefined::operator!=( const QgsDataDefined &other ) const
QgsDataDefined &QgsDataDefined::operator=( const QgsDataDefined & rhs )
{
if ( &rhs == this )
return *this;
delete mExpression;
mExpression = 0;
mActive = rhs.isActive();
mUseExpression = rhs.useExpression();
mExpressionString = rhs.expressionString();
mField = rhs.field();
mExpressionParams = rhs.expressionParams();
mExpressionPrepared = false;
mExprRefColumns.clear();
d.detach();
d = rhs.d;
return *this;
}

View File

@ -18,15 +18,19 @@
#include <QStringList>
#include <QDomElement>
#include <QMap>
#include <QExplicitlySharedDataPointer>
#include "qgis.h"
#include "qgsfield.h"
class QgsExpression;
class QgsVectorLayer;
class QgsDataDefinedPrivate;
/** \ingroup core
* \class QgsDataDefined
* A container class for data source field mapping or expression.
* \note QgsDataDefined objects are implicitly shared.
*/
class CORE_EXPORT QgsDataDefined
@ -84,19 +88,19 @@ class CORE_EXPORT QgsDataDefined
*/
bool hasDefaultValues() const;
bool isActive() const { return mActive; }
void setActive( bool active ) { mActive = active; }
bool isActive() const;
void setActive( bool active );
bool useExpression() const { return mUseExpression; }
bool useExpression() const;
void setUseExpression( bool use );
QString expressionString() const { return mExpressionString; }
QString expressionString() const;
void setExpressionString( const QString& expr );
// @note not available in python bindings
QMap<QString, QVariant> expressionParams() const { return mExpressionParams; }
QMap<QString, QVariant> expressionParams() const;
// @note not available in python bindings
void setExpressionParams( QMap<QString, QVariant> params ) { mExpressionParams = params; }
void setExpressionParams( QMap<QString, QVariant> params );
void insertExpressionParam( QString key, QVariant param );
/** Prepares the expression using a vector layer
@ -115,9 +119,9 @@ class CORE_EXPORT QgsDataDefined
/** Returns whether the data defined object's expression is prepared
* @returns true if expression is prepared
*/
bool expressionIsPrepared() const { return mExpressionPrepared; }
bool expressionIsPrepared() const;
QgsExpression* expression() { return mExpression; }
QgsExpression* expression();
/** Returns the columns referenced by the QgsDataDefined
* @param layer vector layer, used for preparing the expression if required
@ -130,7 +134,7 @@ class CORE_EXPORT QgsDataDefined
*/
QStringList referencedColumns( const QgsFields& fields = QgsFields() );
QString field() const { return mField; }
QString field() const;
void setField( const QString& field );
/** Encodes the QgsDataDefined into a string map.
@ -138,7 +142,7 @@ class CORE_EXPORT QgsDataDefined
* to differentiate multiple QgsDataDefineds encoded in the same string map.
* @see fromMap
*/
QgsStringMap toMap( const QString& baseName = QString() );
QgsStringMap toMap( const QString& baseName = QString() ) const;
/**Returns a DOM element containing the properties of the data defined container.
* @param document DOM document
@ -167,16 +171,8 @@ class CORE_EXPORT QgsDataDefined
QgsDataDefined& operator=( QgsDataDefined const & rhs );
private:
QgsExpression* mExpression;
bool mActive;
bool mUseExpression;
QString mExpressionString;
QString mField;
QMap<QString, QVariant> mExpressionParams;
bool mExpressionPrepared;
QStringList mExprRefColumns;
QExplicitlySharedDataPointer<QgsDataDefinedPrivate> d;
};

View File

@ -0,0 +1,88 @@
/***************************************************************************
qgsdatadefined_p.h
-----------------
Date : May-2015
Copyright : (C) 2015 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSDATADEFINED_PRIVATE_H
#define QGSDATADEFINED_PRIVATE_H
/// @cond
//
// W A R N I N G
// -------------
//
// This file is not part of the QGIS API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
#include <QStringList>
#include <QMap>
#include <QSharedData>
#include "qgsexpression.h"
class CORE_EXPORT QgsDataDefinedPrivate : public QSharedData
{
public:
QgsDataDefinedPrivate( bool active = false,
bool useExpression = false,
const QString& expressionString = QString(),
const QString& field = QString() )
: expression( 0 )
, active( active )
, useExpression( useExpression )
, expressionString( expressionString )
, field( field )
, expressionPrepared( false )
{
}
QgsDataDefinedPrivate( const QgsDataDefinedPrivate& other )
: QSharedData( other )
, expression( 0 )
, active( other.active )
, useExpression( other.useExpression )
, expressionString( other.expressionString )
, field( other.field )
, expressionParams( other.expressionParams )
, expressionPrepared( false )
, exprRefColumns( other.exprRefColumns )
{
}
~QgsDataDefinedPrivate()
{
delete expression;
}
bool operator==( const QgsDataDefinedPrivate& other ) const
{
return (( active == other.active ) && ( useExpression == other.useExpression )
&& ( expressionString == other.expressionString ) && ( field == other.field ) );
}
QgsExpression* expression;
bool active;
bool useExpression;
QString expressionString;
QString field;
QMap<QString, QVariant> expressionParams;
bool expressionPrepared;
QStringList exprRefColumns;
};
#endif // QGSDATADEFINED_PRIVATE_H

View File

@ -35,6 +35,7 @@ class TestQgsDataDefined: public QObject
void cleanup();// will be called after every testfunction.
void create();//test creating a data defined container
void copy();// test cpy destruction (double delete)
void assignment();
void gettersSetters(); //test getters and setters
void defaultValues(); //test hasDefaultValues method
void equality(); //test equality operators
@ -92,13 +93,26 @@ void TestQgsDataDefined::create()
void TestQgsDataDefined::copy()
{
QgsDataDefined dd( true, true, QString( "sqrt(2)" ), QString( "field" ) );
dd.prepareExpression( NULL );
QgsDataDefined cpy( dd );
QVERIFY( cpy == dd );
QgsDataDefined assigned;
assigned = dd;
QVERIFY( assigned == dd );
QgsDataDefined original( true, true, QString( "sqrt(2)" ), QString( "field" ) );
original.prepareExpression( NULL );
QgsDataDefined copy( original );
QVERIFY( copy == original );
copy.setActive( false );
QVERIFY( original.isActive() );
QVERIFY( copy != original );
}
void TestQgsDataDefined::assignment()
{
QgsDataDefined original( true, true, QString( "sqrt(2)" ), QString( "field" ) );
QgsDataDefined copy;
copy = original;
QVERIFY( copy == original );
copy.setActive( false );
QVERIFY( original.isActive() );
QVERIFY( copy != original );
}
void TestQgsDataDefined::gettersSetters()