Skip to content

Commit bb64830

Browse files
committedMay 3, 2015
Implicit sharing for QgsFields
1 parent 31e8611 commit bb64830

File tree

6 files changed

+324
-55
lines changed

6 files changed

+324
-55
lines changed
 

‎python/core/qgsfield.sip

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
2-
/**
3-
\class QgsField
4-
\brief Class to encapsulate a field in an attribute table or data source.
5-
6-
QgsField stores metadata about an attribute field, including name, type
7-
length, and if applicable, precision.
1+
/** \class QgsField
2+
* \ingroup core
3+
* Encapsulate a field in an attribute table or data source.
4+
* QgsField stores metadata about an attribute field, including name, type
5+
* length, and if applicable, precision.
6+
* \note QgsField objects are implicitly shared.
87
*/
98

109
class QgsField
@@ -39,7 +38,7 @@ public:
3938
QgsField( const QgsField& other );
4039

4140
//! Destructor
42-
~QgsField();
41+
virtual ~QgsField();
4342

4443
bool operator==( const QgsField& other ) const;
4544
bool operator!=( const QgsField& other ) const;
@@ -175,6 +174,15 @@ public:
175174
}; // class QgsField
176175

177176

177+
/** \class QgsFields
178+
* \ingroup core
179+
* Container of fields for a vector layer.
180+
*
181+
* In addition to storing a list of QgsField instances, it also:
182+
* - allows quick lookups of field names to index in the list
183+
*- keeps track of where the field definition comes from (vector data provider, joined layer or newly added from an editing operation)
184+
* \note QgsFields objects are implicitly shared.
185+
*/
178186

179187
class QgsFields
180188
{
@@ -192,6 +200,16 @@ class QgsFields
192200
OriginExpression //!< field is calculated from an expression
193201
};
194202

203+
/** Constructor for an empty field container
204+
*/
205+
QgsFields();
206+
207+
/** Copy constructor
208+
*/
209+
QgsFields( const QgsFields& other );
210+
211+
virtual ~QgsFields();
212+
195213
//! Remove all fields
196214
void clear();
197215
//! Append a field. The field must have unique name, otherwise it is rejected (returns false)

‎src/core/qgsfield.cpp

Lines changed: 97 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -178,33 +178,54 @@ bool QgsField::convertCompatible( QVariant& v ) const
178178

179179
////////////////////////////////////////////////////////////////////////////
180180

181+
QgsFields::QgsFields()
182+
{
183+
d = new QgsFieldsPrivate( );
184+
}
185+
186+
QgsFields::QgsFields( const QgsFields &other )
187+
: d( other.d )
188+
{
189+
}
190+
191+
QgsFields &QgsFields::operator =( const QgsFields & other )
192+
{
193+
d = other.d;
194+
return *this;
195+
}
196+
197+
QgsFields::~QgsFields()
198+
{
199+
200+
}
201+
181202
void QgsFields::clear()
182203
{
183-
mFields.clear();
184-
mNameToIndex.clear();
204+
d->fields.clear();
205+
d->nameToIndex.clear();
185206
}
186207

187208
bool QgsFields::append( const QgsField& field, FieldOrigin origin, int originIndex )
188209
{
189-
if ( mNameToIndex.contains( field.name() ) )
210+
if ( d->nameToIndex.contains( field.name() ) )
190211
return false;
191212

192213
if ( originIndex == -1 && origin == OriginProvider )
193-
originIndex = mFields.count();
194-
mFields.append( Field( field, origin, originIndex ) );
214+
originIndex = d->fields.count();
215+
d->fields.append( Field( field, origin, originIndex ) );
195216

196-
mNameToIndex.insert( field.name(), mFields.count() - 1 );
217+
d->nameToIndex.insert( field.name(), d->fields.count() - 1 );
197218
return true;
198219
}
199220

200221
bool QgsFields::appendExpressionField( const QgsField& field, int originIndex )
201222
{
202-
if ( mNameToIndex.contains( field.name() ) )
223+
if ( d->nameToIndex.contains( field.name() ) )
203224
return false;
204225

205-
mFields.append( Field( field, OriginExpression, originIndex ) );
226+
d->fields.append( Field( field, OriginExpression, originIndex ) );
206227

207-
mNameToIndex.insert( field.name(), mFields.count() - 1 );
228+
d->nameToIndex.insert( field.name(), d->fields.count() - 1 );
208229
return true;
209230
}
210231

@@ -213,8 +234,8 @@ void QgsFields::remove( int fieldIdx )
213234
if ( !exists( fieldIdx ) )
214235
return;
215236

216-
mNameToIndex.remove( mFields[fieldIdx].field.name() );
217-
mFields.remove( fieldIdx );
237+
d->nameToIndex.remove( d->fields[fieldIdx].field.name() );
238+
d->fields.remove( fieldIdx );
218239
}
219240

220241
void QgsFields::extend( const QgsFields& other )
@@ -225,27 +246,87 @@ void QgsFields::extend( const QgsFields& other )
225246
}
226247
}
227248

249+
bool QgsFields::isEmpty() const
250+
{
251+
return d->fields.isEmpty();
252+
}
253+
254+
int QgsFields::count() const
255+
{
256+
return d->fields.count();
257+
}
258+
259+
int QgsFields::size() const
260+
{
261+
return d->fields.count();
262+
}
263+
264+
bool QgsFields::exists( int i ) const
265+
{
266+
return i >= 0 && i < d->fields.count();
267+
}
268+
269+
QgsField &QgsFields::operator[]( int i )
270+
{
271+
return d->fields[i].field;
272+
}
273+
274+
const QgsField &QgsFields::at( int i ) const
275+
{
276+
return d->fields[i].field;
277+
}
278+
279+
const QgsField &QgsFields::field( int fieldIdx ) const
280+
{
281+
return d->fields[fieldIdx].field;
282+
}
283+
284+
const QgsField &QgsFields::field( const QString &name ) const
285+
{
286+
return d->fields[ indexFromName( name )].field;
287+
}
288+
289+
const QgsField &QgsFields::operator[]( int i ) const
290+
{
291+
return d->fields[i].field;
292+
}
293+
228294
QgsFields::FieldOrigin QgsFields::fieldOrigin( int fieldIdx ) const
229295
{
230296
if ( !exists( fieldIdx ) )
231297
return OriginUnknown;
232298

233-
return mFields[fieldIdx].origin;
299+
return d->fields[fieldIdx].origin;
300+
}
301+
302+
int QgsFields::fieldOriginIndex( int fieldIdx ) const
303+
{
304+
return d->fields[fieldIdx].originIndex;
305+
}
306+
307+
int QgsFields::indexFromName( const QString &name ) const
308+
{
309+
return d->nameToIndex.value( name, -1 );
234310
}
235311

236312
QList<QgsField> QgsFields::toList() const
237313
{
238314
QList<QgsField> lst;
239-
for ( int i = 0; i < mFields.count(); ++i )
240-
lst.append( mFields[i].field );
315+
for ( int i = 0; i < d->fields.count(); ++i )
316+
lst.append( d->fields[i].field );
241317
return lst;
242318
}
243319

320+
bool QgsFields::operator==( const QgsFields &other ) const
321+
{
322+
return d->fields == other.d->fields;
323+
}
324+
244325
int QgsFields::fieldNameIndex( const QString& fieldName ) const
245326
{
246327
for ( int idx = 0; idx < count(); ++idx )
247328
{
248-
if ( QString::compare( mFields[idx].field.name(), fieldName, Qt::CaseInsensitive ) == 0 )
329+
if ( QString::compare( d->fields[idx].field.name(), fieldName, Qt::CaseInsensitive ) == 0 )
249330
{
250331
return idx;
251332
}
@@ -256,7 +337,7 @@ int QgsFields::fieldNameIndex( const QString& fieldName ) const
256337
QgsAttributeList QgsFields::allAttributesList() const
257338
{
258339
QgsAttributeList lst;
259-
for ( int i = 0; i < mFields.count(); ++i )
340+
for ( int i = 0; i < d->fields.count(); ++i )
260341
lst.append( i );
261342
return lst;
262343
}

‎src/core/qgsfield.h

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@ typedef QList<int> QgsAttributeList;
2525

2626
class QgsExpression;
2727
class QgsFieldPrivate;
28+
class QgsFieldsPrivate;
2829

29-
/** \ingroup core
30+
/** \class QgsField
31+
* \ingroup core
3032
* Encapsulate a field in an attribute table or data source.
3133
* QgsField stores metadata about an attribute field, including name, type
3234
* length, and if applicable, precision.
35+
* \note QgsField objects are implicitly shared.
3336
*/
3437

3538
class CORE_EXPORT QgsField
@@ -63,7 +66,7 @@ class CORE_EXPORT QgsField
6366
QgsField& operator =( const QgsField &other );
6467

6568
//! Destructor
66-
~QgsField();
69+
virtual ~QgsField();
6770

6871
bool operator==( const QgsField& other ) const;
6972
bool operator!=( const QgsField& other ) const;
@@ -152,13 +155,14 @@ class CORE_EXPORT QgsField
152155
}; // class QgsField
153156

154157

155-
/**
156-
\ingroup core
157-
Container of fields for a vector layer.
158-
159-
In addition to storing a list of QgsField instances, it also:
160-
- allows quick lookups of field names to index in the list
161-
- keeps track of where the field definition comes from (vector data provider, joined layer or newly added from an editing operation)
158+
/** \class QgsFields
159+
* \ingroup core
160+
* Container of fields for a vector layer.
161+
*
162+
* In addition to storing a list of QgsField instances, it also:
163+
* - allows quick lookups of field names to index in the list
164+
*- keeps track of where the field definition comes from (vector data provider, joined layer or newly added from an editing operation)
165+
* \note QgsFields objects are implicitly shared.
162166
*/
163167
class CORE_EXPORT QgsFields
164168
{
@@ -188,6 +192,20 @@ class CORE_EXPORT QgsFields
188192
int originIndex; //!< index specific to the origin
189193
} Field;
190194

195+
/** Constructor for an empty field container
196+
*/
197+
QgsFields();
198+
199+
/** Copy constructor
200+
*/
201+
QgsFields( const QgsFields& other );
202+
203+
/** Assignment operator
204+
*/
205+
QgsFields& operator =( const QgsFields &other );
206+
207+
virtual ~QgsFields();
208+
191209
//! Remove all fields
192210
void clear();
193211
//! Append a field. The field must have unique name, otherwise it is rejected (returns false)
@@ -200,34 +218,34 @@ class CORE_EXPORT QgsFields
200218
void extend( const QgsFields& other );
201219

202220
//! Check whether the container is empty
203-
inline bool isEmpty() const { return mFields.isEmpty(); }
221+
bool isEmpty() const;
204222
//! Return number of items
205-
inline int count() const { return mFields.count(); }
223+
int count() const;
206224
//! Return number of items
207-
inline int size() const { return mFields.count(); }
225+
int size() const;
208226
//! Return if a field index is valid
209227
//! @param i Index of the field which needs to be checked
210228
//! @return True if the field exists
211-
inline bool exists( int i ) const { return i >= 0 && i < mFields.count(); }
229+
bool exists( int i ) const;
212230

213231
//! Get field at particular index (must be in range 0..N-1)
214-
inline const QgsField& operator[]( int i ) const { return mFields[i].field; }
232+
const QgsField& operator[]( int i ) const;
215233
//! Get field at particular index (must be in range 0..N-1)
216-
inline QgsField& operator[]( int i ) { return mFields[i].field; }
234+
QgsField& operator[]( int i );
217235
//! Get field at particular index (must be in range 0..N-1)
218-
const QgsField& at( int i ) const { return mFields[i].field; }
236+
const QgsField& at( int i ) const;
219237
//! Get field at particular index (must be in range 0..N-1)
220-
const QgsField& field( int fieldIdx ) const { return mFields[fieldIdx].field; }
238+
const QgsField& field( int fieldIdx ) const;
221239
//! Get field at particular index (must be in range 0..N-1)
222-
const QgsField& field( const QString& name ) const { return mFields[ indexFromName( name )].field; }
240+
const QgsField& field( const QString& name ) const;
223241

224242
//! Get field's origin (value from an enumeration)
225243
FieldOrigin fieldOrigin( int fieldIdx ) const;
226244
//! Get field's origin index (its meaning is specific to each type of origin)
227-
int fieldOriginIndex( int fieldIdx ) const { return mFields[fieldIdx].originIndex; }
245+
int fieldOriginIndex( int fieldIdx ) const;
228246

229247
//! Look up field's index from name. Returns -1 on error
230-
int indexFromName( const QString& name ) const { return mNameToIndex.value( name, -1 ); }
248+
int indexFromName( const QString& name ) const;
231249

232250
//! Look up field's index from name - case insensitive
233251
//! TODO: sort out case sensitive (indexFromName()) vs insensitive (fieldNameIndex()) calls
@@ -242,20 +260,14 @@ class CORE_EXPORT QgsFields
242260
QList<QgsField> toList() const;
243261

244262
//! @note added in 2.6
245-
bool operator==( const QgsFields& other ) const { return mFields == other.mFields; }
263+
bool operator==( const QgsFields& other ) const;
246264
//! @note added in 2.6
247265
bool operator!=( const QgsFields& other ) const { return !( *this == other ); }
248266

249-
protected:
250-
//! internal storage of the container
251-
QVector<Field> mFields;
267+
private:
252268

253-
//! map for quick resolution of name to index
254-
QHash<QString, int> mNameToIndex;
269+
QSharedDataPointer<QgsFieldsPrivate> d;
255270

256271
};
257272

258-
259-
260-
261273
#endif

‎src/core/qgsfield_p.h

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@
3030
#include <QString>
3131
#include <QVariant>
3232
#include <QSharedData>
33+
#include "qgsfield.h"
3334

34-
class QgsFieldPrivate : public QSharedData
35+
class CORE_EXPORT QgsFieldPrivate : public QSharedData
3536
{
3637
public:
3738

@@ -47,7 +48,8 @@ class QgsFieldPrivate : public QSharedData
4748
, length( len )
4849
, precision( prec )
4950
, comment( comment )
50-
{}
51+
{
52+
}
5153

5254
QgsFieldPrivate( const QgsFieldPrivate& other )
5355
: QSharedData( other )
@@ -87,6 +89,32 @@ class QgsFieldPrivate : public QSharedData
8789
QString comment;
8890
};
8991

92+
93+
class CORE_EXPORT QgsFieldsPrivate : public QSharedData
94+
{
95+
public:
96+
97+
QgsFieldsPrivate()
98+
{
99+
}
100+
101+
QgsFieldsPrivate( const QgsFieldsPrivate& other )
102+
: QSharedData( other )
103+
, fields( other.fields )
104+
, nameToIndex( other.nameToIndex )
105+
{
106+
}
107+
108+
~QgsFieldsPrivate() {}
109+
110+
//! internal storage of the container
111+
QVector<QgsFields::Field> fields;
112+
113+
//! map for quick resolution of name to index
114+
QHash<QString, int> nameToIndex;
115+
116+
};
117+
90118
/// @endcond
91119

92120
#endif

‎tests/src/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ ADD_QGIS_TEST(diagramtest testqgsdiagram.cpp)
8484
ADD_QGIS_TEST(diagramexpressiontest testqgsdiagramexpression.cpp)
8585
ADD_QGIS_TEST(expressiontest testqgsexpression.cpp)
8686
ADD_QGIS_TEST(fieldtest testqgsfield.cpp)
87+
ADD_QGIS_TEST(fieldstest testqgsfields.cpp)
8788
ADD_QGIS_TEST(scaleexpressiontest testqgsscaleexpression.cpp)
8889
ADD_QGIS_TEST(filewritertest testqgsvectorfilewriter.cpp)
8990
ADD_QGIS_TEST(projecttest testqgsproject.cpp)

‎tests/src/core/testqgsfields.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/***************************************************************************
2+
testqgsfields.cpp
3+
-----------------
4+
Date : May 2015
5+
Copyright : (C) 2015 Nyall Dawson
6+
Email : nyall dot dawson at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
#include <QtTest/QtTest>
16+
#include <QObject>
17+
#include <QString>
18+
#include <QStringList>
19+
#include <QSettings>
20+
#include <QSharedPointer>
21+
22+
#include "qgsfield.h"
23+
24+
class TestQgsFields: public QObject
25+
{
26+
Q_OBJECT
27+
28+
private slots:
29+
void initTestCase();// will be called before the first testfunction is executed.
30+
void cleanupTestCase();// will be called after the last testfunction was executed.
31+
void init();// will be called before each testfunction is executed.
32+
void cleanup();// will be called after every testfunction.
33+
void create();//test creating a data defined container
34+
void copy();// test cpy destruction (double delete)
35+
void assignment();
36+
void equality(); //test equality operators
37+
private:
38+
};
39+
40+
void TestQgsFields::initTestCase()
41+
{
42+
43+
}
44+
45+
void TestQgsFields::cleanupTestCase()
46+
{
47+
48+
}
49+
50+
void TestQgsFields::init()
51+
{
52+
53+
}
54+
55+
void TestQgsFields::cleanup()
56+
{
57+
58+
}
59+
60+
void TestQgsFields::create()
61+
{
62+
QgsFields fields;
63+
QCOMPARE( fields.count(), 0 );
64+
}
65+
66+
void TestQgsFields::copy()
67+
{
68+
QgsFields original;
69+
//add field
70+
QgsField field( "testfield" );
71+
original.append( field );
72+
QCOMPARE( original.count(), 1 );
73+
QgsFields copy( original );
74+
QCOMPARE( copy.count(), 1 );
75+
QVERIFY( copy == original );
76+
77+
QgsField copyfield( "copyfield" );
78+
copy.append( copyfield );
79+
QCOMPARE( copy.count(), 2 );
80+
QCOMPARE( original.count(), 1 );
81+
QVERIFY( copy != original );
82+
}
83+
84+
void TestQgsFields::assignment()
85+
{
86+
QgsFields original;
87+
//add field
88+
QgsField field( "testfield" );
89+
original.append( field );
90+
91+
QgsFields copy;
92+
copy = original;
93+
QVERIFY( copy == original );
94+
95+
QgsField copyfield( "copyfield" );
96+
copy.append( copyfield );
97+
QCOMPARE( original.count(), 1 );
98+
QCOMPARE( copy.count(), 2 );
99+
QVERIFY( copy != original );
100+
}
101+
102+
void TestQgsFields::equality()
103+
{
104+
//compare two empty QgsFields
105+
QgsFields fields1;
106+
QgsFields fields2;
107+
QVERIFY( fields1 == fields2 );
108+
QVERIFY( !( fields1 != fields2 ) );
109+
110+
//append an identical fields to both and retest
111+
QgsField field1;
112+
field1.setName( "name" );
113+
QgsField field2;
114+
field2.setName( "name" );
115+
QCOMPARE( field1, field2 );
116+
fields1.append( field1 );
117+
fields2.append( field2 );
118+
QVERIFY( fields1 == fields2 );
119+
QVERIFY( !( fields1 != fields2 ) );
120+
121+
//make a change and retest
122+
QgsField field3;
123+
fields2.append( field3 );
124+
QVERIFY( !( fields1 == fields2 ) );
125+
QVERIFY( fields1 != fields2 );
126+
}
127+
128+
QTEST_MAIN( TestQgsFields )
129+
#include "testqgsfields.moc"

0 commit comments

Comments
 (0)
Please sign in to comment.