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 /** \ingroup core
* \class QgsDataDefined * \class QgsDataDefined
* A container class for data source field mapping or expression. * A container class for data source field mapping or expression.
* \note QgsDataDefined objects are implicitly shared.
*/ */
class QgsDataDefined class QgsDataDefined
@ -115,7 +116,7 @@ class QgsDataDefined
* to differentiate multiple QgsDataDefineds encoded in the same string map. * to differentiate multiple QgsDataDefineds encoded in the same string map.
* @see fromMap * @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. /**Returns a DOM element containing the properties of the data defined container.
* @param document DOM document * @param document DOM document

View File

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

View File

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

View File

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

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