QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgsfieldmappingmodel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfieldmappingmodel.cpp - QgsFieldMappingModel
3 
4  ---------------------
5  begin : 17.3.2020
6  copyright : (C) 2020 by Alessandro Pasotti
7  email : elpaso at itopen dot it
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
17 #include "qgsfieldmappingmodel.h"
19 #include "qgsexpressionnodeimpl.h"
20 
22  const QgsFields &destinationFields,
23  const QMap<QString, QString> &expressions,
24  QObject *parent )
25  : QAbstractTableModel( parent )
26  , mSourceFields( sourceFields )
27  , mExpressionContextGenerator( new ExpressionContextGenerator( mSourceFields ) )
28 {
29  setDestinationFields( destinationFields, expressions );
30 }
31 
32 QVariant QgsFieldMappingModel::headerData( int section, Qt::Orientation orientation, int role ) const
33 {
34  if ( role == Qt::DisplayRole )
35  {
36  switch ( orientation )
37  {
38  case Qt::Horizontal:
39  {
40  switch ( static_cast<ColumnDataIndex>( section ) )
41  {
43  {
44  return tr( "Source Expression" );
45  }
47  {
48  return tr( "Name" );
49  }
51  {
52  return tr( "Type" );
53  }
55  {
56  return tr( "Length" );
57  }
59  {
60  return tr( "Precision" );
61  }
63  {
64  return tr( "Constraints" );
65  }
66  }
67  break;
68  }
69  case Qt::Vertical:
70  {
71  return section;
72  }
73  }
74  }
75  return QVariant();
76 }
77 
79 {
80  return mSourceFields;
81 }
82 
83 int QgsFieldMappingModel::rowCount( const QModelIndex &parent ) const
84 {
85  if ( parent.isValid() )
86  return 0;
87  return mMapping.count();
88 }
89 
90 int QgsFieldMappingModel::columnCount( const QModelIndex &parent ) const
91 {
92  if ( parent.isValid() )
93  return 0;
94  return 6;
95 }
96 
97 QVariant QgsFieldMappingModel::data( const QModelIndex &index, int role ) const
98 {
99  if ( index.isValid() )
100  {
101  const ColumnDataIndex col { static_cast<ColumnDataIndex>( index.column() ) };
102  const Field &f { mMapping.at( index.row() ) };
103 
104  const QgsFieldConstraints::Constraints constraints { fieldConstraints( f.field ) };
105 
106  switch ( role )
107  {
108  case Qt::DisplayRole:
109  case Qt::EditRole:
110  {
111  switch ( col )
112  {
114  {
115  return f.expression;
116  }
118  {
119  return f.field.displayName();
120  }
122  {
123  return static_cast<int>( f.field.type() );
124  }
126  {
127  return f.field.length();
128  }
130  {
131  return f.field.precision();
132  }
134  {
135  return constraints != 0 ? tr( "Constraints active" ) : QString();
136  }
137  }
138  break;
139  }
140  case Qt::ToolTipRole:
141  {
143  constraints != 0 )
144  {
145  QStringList constraintDescription;
146  if ( constraints.testFlag( QgsFieldConstraints::Constraint::ConstraintUnique ) )
147  {
148  constraintDescription.push_back( tr( "Unique" ) );
149  }
150  if ( constraints.testFlag( QgsFieldConstraints::Constraint::ConstraintNotNull ) )
151  {
152  constraintDescription.push_back( tr( "Not null" ) );
153  }
154  if ( constraints.testFlag( QgsFieldConstraints::Constraint::ConstraintExpression ) )
155  {
156  constraintDescription.push_back( tr( "Expression" ) );
157  }
158  return constraintDescription.join( QLatin1String( "<br>" ) );
159  }
160  break;
161  }
162  case Qt::BackgroundRole:
163  {
164  if ( constraints != 0 )
165  {
166  return QBrush( QColor( 255, 224, 178 ) );
167  }
168  break;
169  }
170  }
171  }
172  return QVariant();
173 }
174 
175 Qt::ItemFlags QgsFieldMappingModel::flags( const QModelIndex &index ) const
176 {
177  if ( index.isValid() &&
178  index.column() != static_cast<int>( ColumnDataIndex::DestinationConstraints ) &&
179  ( index.column() == static_cast<int>( ColumnDataIndex::SourceExpression ) || destinationEditable() ) )
180  {
181  return Qt::ItemFlags( Qt::ItemIsSelectable |
182  Qt::ItemIsEditable |
183  Qt::ItemIsEnabled );
184  }
185  return Qt::ItemFlags();
186 }
187 
188 bool QgsFieldMappingModel::setData( const QModelIndex &index, const QVariant &value, int role )
189 {
190  if ( index.isValid() )
191  {
192  if ( role == Qt::EditRole )
193  {
194  Field &f = mMapping[index.row()];
195  switch ( static_cast<ColumnDataIndex>( index.column() ) )
196  {
198  {
199  const QgsExpression exp { value.toString() };
200  f.expression = exp;
201  break;
202  }
204  {
205  f.field.setName( value.toString() );
206  break;
207  }
209  {
210  f.field.setType( static_cast<QVariant::Type>( value.toInt( ) ) );
211  break;
212  }
214  {
215  bool ok;
216  const int length { value.toInt( &ok ) };
217  if ( ok )
218  f.field.setLength( length );
219  break;
220  }
222  {
223  bool ok;
224  const int precision { value.toInt( &ok ) };
225  if ( ok )
227  break;
228  }
230  {
231  // Not editable: do nothing
232  }
233  }
234  emit dataChanged( index, index );
235  }
236  return true;
237  }
238  else
239  {
240  return false;
241  }
242 }
243 
244 QgsFieldConstraints::Constraints QgsFieldMappingModel::fieldConstraints( const QgsField &field ) const
245 {
246  QgsFieldConstraints::Constraints constraints;
247 
248  const QgsFieldConstraints fieldConstraints { field.constraints() };
249 
250  if ( fieldConstraints.constraints() & QgsFieldConstraints::ConstraintNotNull &&
252  constraints.setFlag( QgsFieldConstraints::ConstraintNotNull );
253 
254  if ( fieldConstraints.constraints() & QgsFieldConstraints::ConstraintUnique &&
256  constraints.setFlag( QgsFieldConstraints::ConstraintUnique );
257 
258  if ( fieldConstraints.constraints() & QgsFieldConstraints::ConstraintExpression &&
260  constraints.setFlag( QgsFieldConstraints::ConstraintExpression );
261 
262  return constraints;
263 }
264 
265 bool QgsFieldMappingModel::moveUpOrDown( const QModelIndex &index, bool up )
266 {
267  if ( ! index.isValid() && index.model() == this )
268  return false;
269 
270  // Always swap down
271  const int row { up ? index.row() - 1 : index.row() };
272  // Range checking
273  if ( row < 0 || row + 1 >= rowCount( QModelIndex() ) )
274  {
275  return false;
276  }
277  beginMoveRows( QModelIndex( ), row, row, QModelIndex(), row + 2 );
278 #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
279  mMapping.swap( row, row + 1 );
280 #else
281  mMapping.swapItemsAt( row, row + 1 );
282 #endif
283  endMoveRows();
284  return true;
285 }
286 
287 QString QgsFieldMappingModel::findExpressionForDestinationField( const QgsFieldMappingModel::Field &f, QStringList &excludedFieldNames )
288 {
289  // Search for fields in the source
290  // 1. match by name
291  for ( const QgsField &sf : std::as_const( mSourceFields ) )
292  {
293  if ( sf.name() == f.field.name() )
294  {
295  excludedFieldNames.push_back( sf.name() );
296  return QgsExpression::quotedColumnRef( sf.name() );
297  }
298  }
299  // 2. match by type
300  for ( const QgsField &sf : std::as_const( mSourceFields ) )
301  {
302  if ( excludedFieldNames.contains( sf.name() ) || sf.type() != f.field.type() )
303  continue;
304  excludedFieldNames.push_back( sf.name() );
305  return QgsExpression::quotedColumnRef( sf.name() );
306  }
307  return QString();
308 }
309 
311 {
312  mSourceFields = sourceFields;
313  if ( mExpressionContextGenerator )
314  mExpressionContextGenerator->setSourceFields( mSourceFields );
315  QStringList usedFields;
316  beginResetModel();
317  for ( const Field &f : std::as_const( mMapping ) )
318  {
319  if ( QgsExpression( f.expression ).isField() )
320  {
321  usedFields.push_back( f.expression.mid( 1, f.expression.length() - 2 ) );
322  }
323  }
324  for ( auto it = mMapping.begin(); it != mMapping.end(); ++it )
325  {
326  if ( it->expression.isEmpty() )
327  {
328  const QString expression { findExpressionForDestinationField( *it, usedFields ) };
329  if ( ! expression.isEmpty() )
330  it->expression = expression;
331  }
332  }
333  endResetModel();
334 }
335 
337 {
338  return mExpressionContextGenerator.get();
339 }
340 
342 {
343  mExpressionContextGenerator->setBaseExpressionContextGenerator( generator );
344 }
345 
347  const QMap<QString, QString> &expressions )
348 {
349  beginResetModel();
350  mMapping.clear();
351  // Prepare the model data
352  QStringList usedFields;
353  for ( const QgsField &df : destinationFields )
354  {
355  Field f;
356  f.field = df;
357  f.originalName = df.name();
358  if ( expressions.contains( f.field.name() ) )
359  {
360  f.expression = expressions.value( f.field.name() );
361  const QgsExpression exp { f.expression };
362  // if it's source field
363  if ( exp.isField() &&
364  mSourceFields.names().contains( qgis::setToList( exp.referencedColumns() ).first() ) )
365  {
366  usedFields.push_back( qgis::setToList( exp.referencedColumns() ).first() );
367  }
368  }
369  else
370  {
371  const QString expression { findExpressionForDestinationField( f, usedFields ) };
372  if ( ! expression.isEmpty() )
373  f.expression = expression;
374  }
375  mMapping.push_back( f );
376  }
377  endResetModel();
378 }
379 
381 {
382  return mDestinationEditable;
383 }
384 
385 void QgsFieldMappingModel::setDestinationEditable( bool destinationEditable )
386 {
387  mDestinationEditable = destinationEditable;
388 }
389 
390 const QMap<QVariant::Type, QString> QgsFieldMappingModel::dataTypes()
391 {
392  static const QMap<QVariant::Type, QString> sDataTypes
393  {
394  { QVariant::Type::Int, tr( "Whole number (integer - 32bit)" ) },
395  { QVariant::Type::LongLong, tr( "Whole number (integer - 64bit)" ) },
396  { QVariant::Type::Double, tr( "Decimal number (double)" ) },
397  { QVariant::Type::String, tr( "Text (string)" ) },
398  { QVariant::Type::Date, tr( "Date" ) },
399  { QVariant::Type::Time, tr( "Time" ) },
400  { QVariant::Type::DateTime, tr( "Date & Time" ) },
401  { QVariant::Type::Bool, tr( "Boolean" ) },
402  { QVariant::Type::ByteArray, tr( "Binary object (BLOB)" ) },
403  };
404  return sDataTypes;
405 }
406 
407 QList<QgsFieldMappingModel::Field> QgsFieldMappingModel::mapping() const
408 {
409  return mMapping;
410 }
411 
412 QMap<QString, QgsProperty> QgsFieldMappingModel::fieldPropertyMap() const
413 {
414  QMap< QString, QgsProperty > fieldMap;
415  for ( const QgsFieldMappingModel::Field &field : mMapping )
416  {
417  const QgsExpression exp( field.expression );
418  const bool isField = exp.isField();
419  fieldMap.insert( field.originalName, isField
420  ? QgsProperty::fromField( static_cast<const QgsExpressionNodeColumnRef *>( exp.rootNode() )->name() )
421  : QgsProperty::fromExpression( field.expression ) );
422  }
423  return fieldMap;
424 }
425 
426 void QgsFieldMappingModel::setFieldPropertyMap( const QMap<QString, QgsProperty> &map )
427 {
428  beginResetModel();
429  for ( int i = 0; i < mMapping.count(); ++i )
430  {
431  Field &f = mMapping[i];
432  if ( map.contains( f.field.name() ) )
433  {
434  const QgsProperty prop = map.value( f.field.name() );
435  switch ( prop.propertyType() )
436  {
439  break;
440 
442  f.expression = prop.field();
443  break;
444 
446  f.expression = prop.expressionString();
447  break;
448 
450  f.expression.clear();
451  break;
452  }
453  }
454  else
455  {
456  f.expression.clear();
457  }
458  }
459  endResetModel();
460 }
461 
462 void QgsFieldMappingModel::appendField( const QgsField &field, const QString &expression )
463 {
464  const int lastRow { rowCount( QModelIndex( ) ) };
465  beginInsertRows( QModelIndex(), lastRow, lastRow );
466  Field f;
467  f.field = field;
468  f.expression = expression;
469  f.originalName = field.name();
470  mMapping.push_back( f );
471  endInsertRows( );
472 }
473 
474 bool QgsFieldMappingModel::removeField( const QModelIndex &index )
475 {
476  if ( index.isValid() && index.model() == this && index.row() < rowCount( QModelIndex() ) )
477  {
478  beginRemoveRows( QModelIndex(), index.row(), index.row() );
479  mMapping.removeAt( index.row() );
480  endRemoveRows();
481  return true;
482  }
483  else
484  {
485  return false;
486  }
487 }
488 
489 bool QgsFieldMappingModel::moveUp( const QModelIndex &index )
490 {
491  return moveUpOrDown( index );
492 }
493 
494 bool QgsFieldMappingModel::moveDown( const QModelIndex &index )
495 {
496  return moveUpOrDown( index, false );
497 }
498 
499 QgsFieldMappingModel::ExpressionContextGenerator::ExpressionContextGenerator( const QgsFields &sourceFields )
500  : mSourceFields( sourceFields )
501 {
502 }
503 
504 QgsExpressionContext QgsFieldMappingModel::ExpressionContextGenerator::createExpressionContext() const
505 {
506  if ( mBaseGenerator )
507  {
508  QgsExpressionContext ctx = mBaseGenerator->createExpressionContext();
509  std::unique_ptr< QgsExpressionContextScope > fieldMappingScope = std::make_unique< QgsExpressionContextScope >( tr( "Field Mapping" ) );
510  fieldMappingScope->setFields( mSourceFields );
511  ctx.appendScope( fieldMappingScope.release() );
512  return ctx;
513  }
514  else
515  {
518  ctx.setFields( mSourceFields );
519  QgsFeature feature { mSourceFields };
520  feature.setValid( true );
521  ctx.setFeature( feature );
522  return ctx;
523  }
524 }
525 
526 void QgsFieldMappingModel::ExpressionContextGenerator::setBaseExpressionContextGenerator( const QgsExpressionContextGenerator *generator )
527 {
528  mBaseGenerator = generator;
529 }
530 
531 void QgsFieldMappingModel::ExpressionContextGenerator::setSourceFields( const QgsFields &fields )
532 {
533  mSourceFields = fields;
534 }
Abstract interface for generating an expression context.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
An expression node which takes it value from a feature's field.
QString name() const
The name of the column.
Class for parsing and evaluation of expressions (formerly called "search strings").
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
bool isField() const
Checks whether an expression consists only of a single field reference.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
const QgsExpressionNode * rootNode() const
Returns the root node of the expression.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:196
Stores information about constraints which may be present on a field.
@ ConstraintStrengthHard
Constraint must be honored before feature can be accepted.
@ ConstraintNotNull
Field may not be null.
@ ConstraintUnique
Field must have a unique value.
@ ConstraintExpression
Field has an expression constraint set. See constraintExpression().
void setDestinationEditable(bool editable)
Sets the destination fields editable state to editable.
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
ColumnDataIndex
The ColumnDataIndex enum represents the column index for the view.
@ DestinationPrecision
Destination field precision.
@ DestinationConstraints
Destination field constraints.
@ DestinationName
Destination field name.
@ DestinationType
Destination field QVariant::Type casted to (int)
@ DestinationLength
Destination field length.
Qt::ItemFlags flags(const QModelIndex &index) const override
static const QMap< QVariant::Type, QString > dataTypes()
Returns a static map of supported data types.
QgsFields sourceFields() const
Returns a list of source fields.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
bool removeField(const QModelIndex &index)
Removes the field at index from the model, returns true on success.
void appendField(const QgsField &field, const QString &expression=QString())
Appends a new field to the model, with an optional expression.
QMap< QString, QgsProperty > fieldPropertyMap() const
Returns a map of destination field name to QgsProperty definition for field value,...
QList< QgsFieldMappingModel::Field > mapping() const
Returns a list of Field objects representing the current status of the model.
QgsFieldMappingModel(const QgsFields &sourceFields=QgsFields(), const QgsFields &destinationFields=QgsFields(), const QMap< QString, QString > &expressions=QMap< QString, QString >(), QObject *parent=nullptr)
Constructs a QgsFieldMappingModel from a set of sourceFields and destinationFields,...
bool moveDown(const QModelIndex &index)
Moves up the field at index.
bool moveUp(const QModelIndex &index)
Moves down the field at index.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QVariant data(const QModelIndex &index, int role) const override
void setBaseExpressionContextGenerator(const QgsExpressionContextGenerator *generator)
Sets the base expression context generator, which will generate the expression contexts for expressio...
QgsExpressionContextGenerator * contextGenerator() const
Returns the context generator with the source fields.
void setDestinationFields(const QgsFields &destinationFields, const QMap< QString, QString > &expressions=QMap< QString, QString >())
Set destination fields to destinationFields, initial values for the expressions can be optionally spe...
void setSourceFields(const QgsFields &sourceFields)
Set source fields to sourceFields.
bool setData(const QModelIndex &index, const QVariant &value, int role) override
bool destinationEditable() const
Returns true if the destination fields are editable.
void setFieldPropertyMap(const QMap< QString, QgsProperty > &map)
Sets a map of destination field name to QgsProperty definition for field value.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString name
Definition: qgsfield.h:60
void setPrecision(int precision)
Set the field precision.
Definition: qgsfield.cpp:198
void setName(const QString &name)
Set the field name.
Definition: qgsfield.cpp:174
void setLength(int len)
Set the field length.
Definition: qgsfield.cpp:194
QVariant::Type type
Definition: qgsfield.h:58
void setType(QVariant::Type type)
Set variant type.
Definition: qgsfield.cpp:179
QgsFieldConstraints constraints
Definition: qgsfield.h:63
Container of fields for a vector layer.
Definition: qgsfields.h:45
QStringList names() const
Returns a list with field names.
Definition: qgsfields.cpp:143
A store for object properties.
Definition: qgsproperty.h:232
@ ExpressionBasedProperty
Expression based property (QgsExpressionBasedProperty)
Definition: qgsproperty.h:241
@ StaticProperty
Static property (QgsStaticProperty)
Definition: qgsproperty.h:239
@ FieldBasedProperty
Field based property (QgsFieldBasedProperty)
Definition: qgsproperty.h:240
@ InvalidProperty
Invalid (not set) property.
Definition: qgsproperty.h:238
QString expressionString() const
Returns the expression used for the property value.
QString field() const
Returns the current field name the property references.
QVariant value(const QgsExpressionContext &context, const QVariant &defaultValue=QVariant(), bool *ok=nullptr) const
Calculates the current value of the property, including any transforms which are set for the property...
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
QVariant staticValue() const
Returns the current static value for the property.
Type propertyType() const
Returns the property type.
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
const QgsField & field
Definition: qgsfield.h:463
int precision
The Field struct holds information about a mapped field.
QgsField field
The field in its current status (it might have been renamed)
QString expression
The expression for the mapped field from the source fields.
QString originalName
The original name of the field.