LCOV - code coverage report
Current view: top level - core/vector - qgsvectorlayerutils.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 61 675 9.0 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :   qgsvectorlayerutils.cpp
       3                 :            :   -----------------------
       4                 :            :   Date                 : October 2016
       5                 :            :   Copyright            : (C) 2016 by 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                 :            : 
      16                 :            : #include <QRegularExpression>
      17                 :            : 
      18                 :            : #include "qgsexpressioncontext.h"
      19                 :            : #include "qgsfeatureiterator.h"
      20                 :            : #include "qgsfeaturerequest.h"
      21                 :            : #include "qgsvectorlayerutils.h"
      22                 :            : #include "qgsvectordataprovider.h"
      23                 :            : #include "qgsproject.h"
      24                 :            : #include "qgsrelationmanager.h"
      25                 :            : #include "qgsfeedback.h"
      26                 :            : #include "qgsvectorlayer.h"
      27                 :            : #include "qgsthreadingutils.h"
      28                 :            : #include "qgsgeometrycollection.h"
      29                 :            : #include "qgsexpressioncontextutils.h"
      30                 :            : #include "qgsmultisurface.h"
      31                 :            : #include "qgsgeometryfactory.h"
      32                 :            : #include "qgscurvepolygon.h"
      33                 :            : #include "qgspolygon.h"
      34                 :            : #include "qgslinestring.h"
      35                 :            : #include "qgsmultipoint.h"
      36                 :            : #include "qgsvectorlayerjoinbuffer.h"
      37                 :            : #include "qgsvectorlayerlabeling.h"
      38                 :            : #include "qgspallabeling.h"
      39                 :            : #include "qgsrenderer.h"
      40                 :            : #include "qgssymbollayer.h"
      41                 :            : #include "qgsstyleentityvisitor.h"
      42                 :            : #include "qgsstyle.h"
      43                 :            : #include "qgsauxiliarystorage.h"
      44                 :            : 
      45                 :            : 
      46                 :          0 : QgsFeatureIterator QgsVectorLayerUtils::getValuesIterator( const QgsVectorLayer *layer, const QString &fieldOrExpression, bool &ok, bool selectedOnly )
      47                 :            : {
      48                 :          0 :   std::unique_ptr<QgsExpression> expression;
      49                 :          0 :   QgsExpressionContext context;
      50                 :            : 
      51                 :          0 :   int attrNum = layer->fields().lookupField( fieldOrExpression );
      52                 :          0 :   if ( attrNum == -1 )
      53                 :            :   {
      54                 :            :     // try to use expression
      55                 :          0 :     expression.reset( new QgsExpression( fieldOrExpression ) );
      56                 :          0 :     context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
      57                 :            : 
      58                 :          0 :     if ( expression->hasParserError() || !expression->prepare( &context ) )
      59                 :            :     {
      60                 :          0 :       ok = false;
      61                 :          0 :       return QgsFeatureIterator();
      62                 :            :     }
      63                 :          0 :   }
      64                 :            : 
      65                 :          0 :   QSet<QString> lst;
      66                 :          0 :   if ( !expression )
      67                 :          0 :     lst.insert( fieldOrExpression );
      68                 :            :   else
      69                 :          0 :     lst = expression->referencedColumns();
      70                 :            : 
      71                 :          0 :   QgsFeatureRequest request = QgsFeatureRequest()
      72                 :          0 :                               .setFlags( ( expression && expression->needsGeometry() ) ?
      73                 :            :                                          QgsFeatureRequest::NoFlags :
      74                 :            :                                          QgsFeatureRequest::NoGeometry )
      75                 :          0 :                               .setSubsetOfAttributes( lst, layer->fields() );
      76                 :            : 
      77                 :          0 :   ok = true;
      78                 :          0 :   if ( !selectedOnly )
      79                 :            :   {
      80                 :          0 :     return layer->getFeatures( request );
      81                 :            :   }
      82                 :            :   else
      83                 :            :   {
      84                 :          0 :     return layer->getSelectedFeatures( request );
      85                 :            :   }
      86                 :          0 : }
      87                 :            : 
      88                 :          0 : QList<QVariant> QgsVectorLayerUtils::getValues( const QgsVectorLayer *layer, const QString &fieldOrExpression, bool &ok, bool selectedOnly, QgsFeedback *feedback )
      89                 :            : {
      90                 :          0 :   QList<QVariant> values;
      91                 :          0 :   QgsFeatureIterator fit = getValuesIterator( layer, fieldOrExpression, ok, selectedOnly );
      92                 :          0 :   if ( ok )
      93                 :            :   {
      94                 :          0 :     std::unique_ptr<QgsExpression> expression;
      95                 :          0 :     QgsExpressionContext context;
      96                 :            : 
      97                 :          0 :     int attrNum = layer->fields().lookupField( fieldOrExpression );
      98                 :          0 :     if ( attrNum == -1 )
      99                 :            :     {
     100                 :            :       // use expression, already validated in the getValuesIterator() function
     101                 :          0 :       expression.reset( new QgsExpression( fieldOrExpression ) );
     102                 :          0 :       context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
     103                 :          0 :     }
     104                 :            : 
     105                 :          0 :     QgsFeature f;
     106                 :          0 :     while ( fit.nextFeature( f ) )
     107                 :            :     {
     108                 :          0 :       if ( expression )
     109                 :            :       {
     110                 :          0 :         context.setFeature( f );
     111                 :          0 :         QVariant v = expression->evaluate( &context );
     112                 :          0 :         values << v;
     113                 :          0 :       }
     114                 :            :       else
     115                 :            :       {
     116                 :          0 :         values << f.attribute( attrNum );
     117                 :            :       }
     118                 :          0 :       if ( feedback && feedback->isCanceled() )
     119                 :            :       {
     120                 :          0 :         ok = false;
     121                 :          0 :         return values;
     122                 :            :       }
     123                 :            :     }
     124                 :          0 :   }
     125                 :          0 :   return values;
     126                 :          0 : }
     127                 :            : 
     128                 :          0 : QList<double> QgsVectorLayerUtils::getDoubleValues( const QgsVectorLayer *layer, const QString &fieldOrExpression, bool &ok, bool selectedOnly, int *nullCount, QgsFeedback *feedback )
     129                 :            : {
     130                 :          0 :   QList<double> values;
     131                 :            : 
     132                 :          0 :   if ( nullCount )
     133                 :          0 :     *nullCount = 0;
     134                 :            : 
     135                 :          0 :   QList<QVariant> variantValues = getValues( layer, fieldOrExpression, ok, selectedOnly, feedback );
     136                 :          0 :   if ( !ok )
     137                 :          0 :     return values;
     138                 :            : 
     139                 :            :   bool convertOk;
     140                 :          0 :   const auto constVariantValues = variantValues;
     141                 :          0 :   for ( const QVariant &value : constVariantValues )
     142                 :            :   {
     143                 :          0 :     double val = value.toDouble( &convertOk );
     144                 :          0 :     if ( convertOk )
     145                 :          0 :       values << val;
     146                 :          0 :     else if ( value.isNull() )
     147                 :            :     {
     148                 :          0 :       if ( nullCount )
     149                 :          0 :         *nullCount += 1;
     150                 :          0 :     }
     151                 :          0 :     if ( feedback && feedback->isCanceled() )
     152                 :            :     {
     153                 :          0 :       ok = false;
     154                 :          0 :       return values;
     155                 :            :     }
     156                 :            :   }
     157                 :          0 :   return values;
     158                 :          0 : }
     159                 :            : 
     160                 :          0 : bool QgsVectorLayerUtils::valueExists( const QgsVectorLayer *layer, int fieldIndex, const QVariant &value, const QgsFeatureIds &ignoreIds )
     161                 :            : {
     162                 :          0 :   if ( !layer )
     163                 :          0 :     return false;
     164                 :            : 
     165                 :          0 :   QgsFields fields = layer->fields();
     166                 :            : 
     167                 :          0 :   if ( fieldIndex < 0 || fieldIndex >= fields.count() )
     168                 :          0 :     return false;
     169                 :            : 
     170                 :            :   // If it's a joined field search the value in the source layer
     171                 :          0 :   if ( fields.fieldOrigin( fieldIndex ) == QgsFields::FieldOrigin::OriginJoin )
     172                 :            :   {
     173                 :            :     int srcFieldIndex;
     174                 :          0 :     const QgsVectorLayerJoinInfo *joinInfo { layer->joinBuffer()->joinForFieldIndex( fieldIndex, fields, srcFieldIndex ) };
     175                 :          0 :     if ( ! joinInfo )
     176                 :            :     {
     177                 :          0 :       return false;
     178                 :            :     }
     179                 :          0 :     fieldIndex = srcFieldIndex;
     180                 :          0 :     layer = joinInfo->joinLayer();
     181                 :          0 :     if ( ! layer )
     182                 :            :     {
     183                 :          0 :       return false;
     184                 :            :     }
     185                 :          0 :     fields = layer->fields();
     186                 :          0 :   }
     187                 :            : 
     188                 :          0 :   QString fieldName = fields.at( fieldIndex ).name();
     189                 :            : 
     190                 :            :   // build up an optimised feature request
     191                 :          0 :   QgsFeatureRequest request;
     192                 :          0 :   request.setNoAttributes();
     193                 :          0 :   request.setFlags( QgsFeatureRequest::NoGeometry );
     194                 :            : 
     195                 :            :   // at most we need to check ignoreIds.size() + 1 - the feature not in ignoreIds is the one we're interested in
     196                 :          0 :   int limit = ignoreIds.size() + 1;
     197                 :          0 :   request.setLimit( limit );
     198                 :            : 
     199                 :          0 :   request.setFilterExpression( QStringLiteral( "%1=%2" ).arg( QgsExpression::quotedColumnRef( fieldName ),
     200                 :          0 :                                QgsExpression::quotedValue( value ) ) );
     201                 :            : 
     202                 :          0 :   QgsFeature feat;
     203                 :          0 :   QgsFeatureIterator it = layer->getFeatures( request );
     204                 :          0 :   while ( it.nextFeature( feat ) )
     205                 :            :   {
     206                 :          0 :     if ( ignoreIds.contains( feat.id() ) )
     207                 :          0 :       continue;
     208                 :            : 
     209                 :          0 :     return true;
     210                 :            :   }
     211                 :            : 
     212                 :          0 :   return false;
     213                 :          0 : }
     214                 :            : 
     215                 :          0 : QVariant QgsVectorLayerUtils::createUniqueValue( const QgsVectorLayer *layer, int fieldIndex, const QVariant &seed )
     216                 :            : {
     217                 :          0 :   if ( !layer )
     218                 :          0 :     return QVariant();
     219                 :            : 
     220                 :          0 :   QgsFields fields = layer->fields();
     221                 :            : 
     222                 :          0 :   if ( fieldIndex < 0 || fieldIndex >= fields.count() )
     223                 :          0 :     return QVariant();
     224                 :            : 
     225                 :          0 :   QgsField field = fields.at( fieldIndex );
     226                 :            : 
     227                 :          0 :   if ( field.isNumeric() )
     228                 :            :   {
     229                 :          0 :     QVariant maxVal = layer->maximumValue( fieldIndex );
     230                 :          0 :     QVariant newVar( maxVal.toLongLong() + 1 );
     231                 :          0 :     if ( field.convertCompatible( newVar ) )
     232                 :          0 :       return newVar;
     233                 :            :     else
     234                 :          0 :       return QVariant();
     235                 :          0 :   }
     236                 :            :   else
     237                 :            :   {
     238                 :          0 :     switch ( field.type() )
     239                 :            :     {
     240                 :            :       case QVariant::String:
     241                 :            :       {
     242                 :          0 :         QString base;
     243                 :          0 :         if ( seed.isValid() )
     244                 :          0 :           base = seed.toString();
     245                 :            : 
     246                 :          0 :         if ( !base.isEmpty() )
     247                 :            :         {
     248                 :            :           // strip any existing _1, _2 from the seed
     249                 :          0 :           QRegularExpression rx( QStringLiteral( "(.*)_\\d+" ) );
     250                 :          0 :           QRegularExpressionMatch match = rx.match( base );
     251                 :          0 :           if ( match.hasMatch() )
     252                 :            :           {
     253                 :          0 :             base = match.captured( 1 );
     254                 :          0 :           }
     255                 :          0 :         }
     256                 :            :         else
     257                 :            :         {
     258                 :            :           // no base seed - fetch first value from layer
     259                 :          0 :           QgsFeatureRequest req;
     260                 :          0 :           req.setLimit( 1 );
     261                 :          0 :           req.setSubsetOfAttributes( QgsAttributeList() << fieldIndex );
     262                 :          0 :           req.setFlags( QgsFeatureRequest::NoGeometry );
     263                 :          0 :           QgsFeature f;
     264                 :          0 :           layer->getFeatures( req ).nextFeature( f );
     265                 :          0 :           base = f.attribute( fieldIndex ).toString();
     266                 :          0 :         }
     267                 :            : 
     268                 :            :         // try variants like base_1, base_2, etc until a new value found
     269                 :          0 :         QStringList vals = layer->uniqueStringsMatching( fieldIndex, base );
     270                 :            : 
     271                 :            :         // might already be unique
     272                 :          0 :         if ( !base.isEmpty() && !vals.contains( base ) )
     273                 :          0 :           return base;
     274                 :            : 
     275                 :          0 :         for ( int i = 1; i < 10000; ++i )
     276                 :            :         {
     277                 :          0 :           QString testVal = base + '_' + QString::number( i );
     278                 :          0 :           if ( !vals.contains( testVal ) )
     279                 :          0 :             return testVal;
     280                 :          0 :         }
     281                 :            : 
     282                 :            :         // failed
     283                 :          0 :         return QVariant();
     284                 :          0 :       }
     285                 :            : 
     286                 :            :       default:
     287                 :            :         // todo other types - dates? times?
     288                 :          0 :         break;
     289                 :            :     }
     290                 :            :   }
     291                 :            : 
     292                 :          0 :   return QVariant();
     293                 :          0 : }
     294                 :            : 
     295                 :          0 : QVariant QgsVectorLayerUtils::createUniqueValueFromCache( const QgsVectorLayer *layer, int fieldIndex, const QSet<QVariant> &existingValues, const QVariant &seed )
     296                 :            : {
     297                 :          0 :   if ( !layer )
     298                 :          0 :     return QVariant();
     299                 :            : 
     300                 :          0 :   QgsFields fields = layer->fields();
     301                 :            : 
     302                 :          0 :   if ( fieldIndex < 0 || fieldIndex >= fields.count() )
     303                 :          0 :     return QVariant();
     304                 :            : 
     305                 :          0 :   QgsField field = fields.at( fieldIndex );
     306                 :            : 
     307                 :          0 :   if ( field.isNumeric() )
     308                 :            :   {
     309                 :          0 :     QVariant maxVal = existingValues.isEmpty() ? 0 : *std::max_element( existingValues.begin(), existingValues.end(), []( const QVariant & a, const QVariant & b ) { return a.toLongLong() < b.toLongLong(); } );
     310                 :          0 :     QVariant newVar( maxVal.toLongLong() + 1 );
     311                 :          0 :     if ( field.convertCompatible( newVar ) )
     312                 :          0 :       return newVar;
     313                 :            :     else
     314                 :          0 :       return QVariant();
     315                 :          0 :   }
     316                 :            :   else
     317                 :            :   {
     318                 :          0 :     switch ( field.type() )
     319                 :            :     {
     320                 :            :       case QVariant::String:
     321                 :            :       {
     322                 :          0 :         QString base;
     323                 :          0 :         if ( seed.isValid() )
     324                 :          0 :           base = seed.toString();
     325                 :            : 
     326                 :          0 :         if ( !base.isEmpty() )
     327                 :            :         {
     328                 :            :           // strip any existing _1, _2 from the seed
     329                 :          0 :           QRegularExpression rx( QStringLiteral( "(.*)_\\d+" ) );
     330                 :          0 :           QRegularExpressionMatch match = rx.match( base );
     331                 :          0 :           if ( match.hasMatch() )
     332                 :            :           {
     333                 :          0 :             base = match.captured( 1 );
     334                 :          0 :           }
     335                 :          0 :         }
     336                 :            :         else
     337                 :            :         {
     338                 :            :           // no base seed - fetch first value from layer
     339                 :          0 :           QgsFeatureRequest req;
     340                 :          0 :           base = existingValues.isEmpty() ? QString() : existingValues.values().first().toString();
     341                 :          0 :         }
     342                 :            : 
     343                 :            :         // try variants like base_1, base_2, etc until a new value found
     344                 :          0 :         QStringList vals;
     345                 :          0 :         for ( const auto &v : std::as_const( existingValues ) )
     346                 :            :         {
     347                 :          0 :           if ( v.toString().startsWith( base ) )
     348                 :          0 :             vals.push_back( v.toString() );
     349                 :            :         }
     350                 :            : 
     351                 :            :         // might already be unique
     352                 :          0 :         if ( !base.isEmpty() && !vals.contains( base ) )
     353                 :          0 :           return base;
     354                 :            : 
     355                 :          0 :         for ( int i = 1; i < 10000; ++i )
     356                 :            :         {
     357                 :          0 :           QString testVal = base + '_' + QString::number( i );
     358                 :          0 :           if ( !vals.contains( testVal ) )
     359                 :          0 :             return testVal;
     360                 :          0 :         }
     361                 :            : 
     362                 :            :         // failed
     363                 :          0 :         return QVariant();
     364                 :          0 :       }
     365                 :            : 
     366                 :            :       default:
     367                 :            :         // todo other types - dates? times?
     368                 :          0 :         break;
     369                 :            :     }
     370                 :            :   }
     371                 :            : 
     372                 :          0 :   return QVariant();
     373                 :            : 
     374                 :          0 : }
     375                 :            : 
     376                 :          0 : bool QgsVectorLayerUtils::validateAttribute( const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors,
     377                 :            :     QgsFieldConstraints::ConstraintStrength strength, QgsFieldConstraints::ConstraintOrigin origin )
     378                 :            : {
     379                 :          0 :   if ( !layer )
     380                 :          0 :     return false;
     381                 :            : 
     382                 :          0 :   if ( attributeIndex < 0 || attributeIndex >= layer->fields().count() )
     383                 :          0 :     return false;
     384                 :            : 
     385                 :          0 :   QgsFields fields = layer->fields();
     386                 :          0 :   QgsField field = fields.at( attributeIndex );
     387                 :          0 :   QVariant value = feature.attribute( attributeIndex );
     388                 :          0 :   bool valid = true;
     389                 :          0 :   errors.clear();
     390                 :            : 
     391                 :          0 :   QgsFieldConstraints constraints = field.constraints();
     392                 :            : 
     393                 :          0 :   if ( constraints.constraints() & QgsFieldConstraints::ConstraintExpression && !constraints.constraintExpression().isEmpty()
     394                 :          0 :        && ( strength == QgsFieldConstraints::ConstraintStrengthNotSet || strength == constraints.constraintStrength( QgsFieldConstraints::ConstraintExpression ) )
     395                 :          0 :        && ( origin == QgsFieldConstraints::ConstraintOriginNotSet || origin == constraints.constraintOrigin( QgsFieldConstraints::ConstraintExpression ) ) )
     396                 :            :   {
     397                 :          0 :     QgsExpressionContext context = layer->createExpressionContext();
     398                 :          0 :     context.setFeature( feature );
     399                 :            : 
     400                 :          0 :     QgsExpression expr( constraints.constraintExpression() );
     401                 :            : 
     402                 :          0 :     valid = expr.evaluate( &context ).toBool();
     403                 :            : 
     404                 :          0 :     if ( expr.hasParserError() )
     405                 :            :     {
     406                 :          0 :       errors << QObject::tr( "parser error: %1" ).arg( expr.parserErrorString() );
     407                 :          0 :     }
     408                 :          0 :     else if ( expr.hasEvalError() )
     409                 :            :     {
     410                 :          0 :       errors << QObject::tr( "evaluation error: %1" ).arg( expr.evalErrorString() );
     411                 :          0 :     }
     412                 :          0 :     else if ( !valid )
     413                 :            :     {
     414                 :          0 :       errors << QObject::tr( "%1 check failed" ).arg( constraints.constraintDescription() );
     415                 :          0 :     }
     416                 :          0 :   }
     417                 :            : 
     418                 :          0 :   bool notNullConstraintViolated { false };
     419                 :            : 
     420                 :          0 :   if ( constraints.constraints() & QgsFieldConstraints::ConstraintNotNull
     421                 :          0 :        && ( strength == QgsFieldConstraints::ConstraintStrengthNotSet || strength == constraints.constraintStrength( QgsFieldConstraints::ConstraintNotNull ) )
     422                 :          0 :        && ( origin == QgsFieldConstraints::ConstraintOriginNotSet || origin == constraints.constraintOrigin( QgsFieldConstraints::ConstraintNotNull ) ) )
     423                 :            :   {
     424                 :          0 :     bool exempt = false;
     425                 :          0 :     if ( fields.fieldOrigin( attributeIndex ) == QgsFields::OriginProvider
     426                 :          0 :          && constraints.constraintOrigin( QgsFieldConstraints::ConstraintNotNull ) == QgsFieldConstraints::ConstraintOriginProvider )
     427                 :            :     {
     428                 :          0 :       int providerIdx = fields.fieldOriginIndex( attributeIndex );
     429                 :          0 :       exempt = layer->dataProvider()->skipConstraintCheck( providerIdx, QgsFieldConstraints::ConstraintNotNull, value );
     430                 :          0 :     }
     431                 :            : 
     432                 :          0 :     if ( !exempt )
     433                 :            :     {
     434                 :          0 :       valid = valid && !value.isNull();
     435                 :            : 
     436                 :          0 :       if ( value.isNull() )
     437                 :            :       {
     438                 :          0 :         errors << QObject::tr( "value is NULL" );
     439                 :          0 :         notNullConstraintViolated = true;
     440                 :          0 :       }
     441                 :          0 :     }
     442                 :          0 :   }
     443                 :            : 
     444                 :            :   // if a NOT NULL constraint is violated we don't need to check for UNIQUE
     445                 :          0 :   if ( ! notNullConstraintViolated )
     446                 :            :   {
     447                 :            : 
     448                 :          0 :     if ( constraints.constraints() & QgsFieldConstraints::ConstraintUnique
     449                 :          0 :          && ( strength == QgsFieldConstraints::ConstraintStrengthNotSet || strength == constraints.constraintStrength( QgsFieldConstraints::ConstraintUnique ) )
     450                 :          0 :          && ( origin == QgsFieldConstraints::ConstraintOriginNotSet || origin == constraints.constraintOrigin( QgsFieldConstraints::ConstraintUnique ) ) )
     451                 :            :     {
     452                 :          0 :       bool exempt = false;
     453                 :          0 :       if ( fields.fieldOrigin( attributeIndex ) == QgsFields::OriginProvider
     454                 :          0 :            && constraints.constraintOrigin( QgsFieldConstraints::ConstraintNotNull ) == QgsFieldConstraints::ConstraintOriginProvider )
     455                 :            :       {
     456                 :          0 :         int providerIdx = fields.fieldOriginIndex( attributeIndex );
     457                 :          0 :         exempt = layer->dataProvider()->skipConstraintCheck( providerIdx, QgsFieldConstraints::ConstraintUnique, value );
     458                 :          0 :       }
     459                 :            : 
     460                 :          0 :       if ( !exempt )
     461                 :            :       {
     462                 :            : 
     463                 :          0 :         bool alreadyExists = QgsVectorLayerUtils::valueExists( layer, attributeIndex, value, QgsFeatureIds() << feature.id() );
     464                 :          0 :         valid = valid && !alreadyExists;
     465                 :            : 
     466                 :          0 :         if ( alreadyExists )
     467                 :            :         {
     468                 :          0 :           errors << QObject::tr( "value is not unique" );
     469                 :          0 :         }
     470                 :          0 :       }
     471                 :          0 :     }
     472                 :          0 :   }
     473                 :            : 
     474                 :          0 :   return valid;
     475                 :          0 : }
     476                 :            : 
     477                 :          1 : QgsFeature QgsVectorLayerUtils::createFeature( const QgsVectorLayer *layer, const QgsGeometry &geometry,
     478                 :            :     const QgsAttributeMap &attributes, QgsExpressionContext *context )
     479                 :            : {
     480                 :          1 :   QgsFeatureList features { createFeatures( layer, QgsFeaturesDataList() << QgsFeatureData( geometry, attributes ), context ) };
     481                 :          1 :   return features.isEmpty() ? QgsFeature() : features.first();
     482                 :          1 : }
     483                 :            : 
     484                 :          1 : QgsFeatureList QgsVectorLayerUtils::createFeatures( const QgsVectorLayer *layer, const QgsFeaturesDataList &featuresData, QgsExpressionContext *context )
     485                 :            : {
     486                 :          1 :   if ( !layer )
     487                 :          0 :     return QgsFeatureList();
     488                 :            : 
     489                 :          1 :   QgsFeatureList result;
     490                 :          1 :   result.reserve( featuresData.length() );
     491                 :            : 
     492                 :          1 :   QgsExpressionContext *evalContext = context;
     493                 :          1 :   std::unique_ptr< QgsExpressionContext > tempContext;
     494                 :          1 :   if ( !evalContext )
     495                 :            :   {
     496                 :            :     // no context passed, so we create a default one
     497                 :          1 :     tempContext.reset( new QgsExpressionContext( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) ) );
     498                 :          1 :     evalContext = tempContext.get();
     499                 :          1 :   }
     500                 :            : 
     501                 :          1 :   QgsFields fields = layer->fields();
     502                 :            : 
     503                 :            :   // Cache unique values
     504                 :          1 :   QMap<int, QSet<QVariant>> uniqueValueCache;
     505                 :            : 
     506                 :          1 :   auto checkUniqueValue = [ & ]( const int fieldIdx, const QVariant & value )
     507                 :            :   {
     508                 :          0 :     if ( ! uniqueValueCache.contains( fieldIdx ) )
     509                 :            :     {
     510                 :            :       // If the layer is filtered, get unique values from an unfiltered clone
     511                 :          0 :       if ( ! layer->subsetString().isEmpty() )
     512                 :            :       {
     513                 :          0 :         std::unique_ptr<QgsVectorLayer> unfilteredClone { layer->clone( ) };
     514                 :          0 :         unfilteredClone->setSubsetString( QString( ) );
     515                 :          0 :         uniqueValueCache[ fieldIdx ] = unfilteredClone->uniqueValues( fieldIdx );
     516                 :          0 :       }
     517                 :            :       else
     518                 :            :       {
     519                 :          0 :         uniqueValueCache[ fieldIdx ] = layer->uniqueValues( fieldIdx );
     520                 :            :       }
     521                 :          0 :     }
     522                 :          0 :     return uniqueValueCache[ fieldIdx ].contains( value );
     523                 :          0 :   };
     524                 :            : 
     525                 :          2 :   for ( const auto &fd : std::as_const( featuresData ) )
     526                 :            :   {
     527                 :            : 
     528                 :          1 :     QgsFeature newFeature( fields );
     529                 :          1 :     newFeature.setValid( true );
     530                 :          1 :     newFeature.setGeometry( fd.geometry() );
     531                 :            : 
     532                 :            :     // initialize attributes
     533                 :          1 :     newFeature.initAttributes( fields.count() );
     534                 :          1 :     for ( int idx = 0; idx < fields.count(); ++idx )
     535                 :            :     {
     536                 :          0 :       QVariant v;
     537                 :          0 :       bool checkUnique = true;
     538                 :          0 :       const bool hasUniqueConstraint { static_cast<bool>( fields.at( idx ).constraints().constraints() & QgsFieldConstraints::ConstraintUnique ) };
     539                 :            : 
     540                 :            :       // in order of priority:
     541                 :            :       // 1. passed attribute value and if field does not have a unique constraint like primary key
     542                 :          0 :       if ( fd.attributes().contains( idx ) )
     543                 :            :       {
     544                 :          0 :         v = fd.attributes().value( idx );
     545                 :          0 :       }
     546                 :            : 
     547                 :            :       // 2. client side default expression
     548                 :            :       // note - deliberately not using else if!
     549                 :          0 :       QgsDefaultValue defaultValueDefinition = layer->defaultValueDefinition( idx );
     550                 :          0 :       if ( ( v.isNull() || ( hasUniqueConstraint
     551                 :          0 :                              && checkUniqueValue( idx, v ) )
     552                 :          0 :              || defaultValueDefinition.applyOnUpdate() )
     553                 :          0 :            && defaultValueDefinition.isValid() )
     554                 :            :       {
     555                 :            :         // client side default expression set - takes precedence over all. Why? Well, this is the only default
     556                 :            :         // which QGIS users have control over, so we assume that they're deliberately overriding any
     557                 :            :         // provider defaults for some good reason and we should respect that
     558                 :          0 :         v = layer->defaultValue( idx, newFeature, evalContext );
     559                 :          0 :       }
     560                 :            : 
     561                 :            :       // 3. provider side default value clause
     562                 :            :       // note - not an else if deliberately. Users may return null from a default value expression to fallback to provider defaults
     563                 :          0 :       if ( ( v.isNull() || ( hasUniqueConstraint
     564                 :          0 :                              && checkUniqueValue( idx, v ) ) )
     565                 :          0 :            && fields.fieldOrigin( idx ) == QgsFields::OriginProvider )
     566                 :            :       {
     567                 :          0 :         int providerIndex = fields.fieldOriginIndex( idx );
     568                 :          0 :         QString providerDefault = layer->dataProvider()->defaultValueClause( providerIndex );
     569                 :          0 :         if ( !providerDefault.isEmpty() )
     570                 :            :         {
     571                 :          0 :           v = providerDefault;
     572                 :          0 :           checkUnique = false;
     573                 :          0 :         }
     574                 :          0 :       }
     575                 :            : 
     576                 :            :       // 4. provider side default literal
     577                 :            :       // note - deliberately not using else if!
     578                 :          0 :       if ( ( v.isNull() || ( checkUnique
     579                 :          0 :                              && hasUniqueConstraint
     580                 :          0 :                              && checkUniqueValue( idx, v ) ) )
     581                 :          0 :            && fields.fieldOrigin( idx ) == QgsFields::OriginProvider )
     582                 :            :       {
     583                 :          0 :         int providerIndex = fields.fieldOriginIndex( idx );
     584                 :          0 :         v = layer->dataProvider()->defaultValue( providerIndex );
     585                 :          0 :         if ( v.isValid() )
     586                 :            :         {
     587                 :            :           //trust that the provider default has been sensibly set not to violate any constraints
     588                 :          0 :           checkUnique = false;
     589                 :          0 :         }
     590                 :          0 :       }
     591                 :            : 
     592                 :            :       // 5. passed attribute value
     593                 :            :       // note - deliberately not using else if!
     594                 :          0 :       if ( v.isNull() && fd.attributes().contains( idx ) )
     595                 :            :       {
     596                 :          0 :         v = fd.attributes().value( idx );
     597                 :          0 :       }
     598                 :            : 
     599                 :            :       // last of all... check that unique constraints are respected if the value is valid
     600                 :          0 :       if ( v.isValid() )
     601                 :            :       {
     602                 :            :         // we can't handle not null or expression constraints here, since there's no way to pick a sensible
     603                 :            :         // value if the constraint is violated
     604                 :          0 :         if ( checkUnique && hasUniqueConstraint )
     605                 :            :         {
     606                 :          0 :           if ( checkUniqueValue( idx,  v ) )
     607                 :            :           {
     608                 :            :             // unique constraint violated
     609                 :          0 :             QVariant uniqueValue = QgsVectorLayerUtils::createUniqueValueFromCache( layer, idx, uniqueValueCache[ idx ], v );
     610                 :          0 :             if ( uniqueValue.isValid() )
     611                 :          0 :               v = uniqueValue;
     612                 :          0 :           }
     613                 :          0 :         }
     614                 :          0 :         if ( hasUniqueConstraint )
     615                 :            :         {
     616                 :          0 :           uniqueValueCache[ idx ].insert( v );
     617                 :          0 :         }
     618                 :          0 :       }
     619                 :          0 :       newFeature.setAttribute( idx, v );
     620                 :          0 :     }
     621                 :          1 :     result.append( newFeature );
     622                 :          1 :   }
     623                 :          1 :   return result;
     624                 :          1 : }
     625                 :            : 
     626                 :          0 : QgsFeature QgsVectorLayerUtils::duplicateFeature( QgsVectorLayer *layer, const QgsFeature &feature, QgsProject *project, QgsDuplicateFeatureContext &duplicateFeatureContext, const int maxDepth, int depth, QList<QgsVectorLayer *> referencedLayersBranch )
     627                 :            : {
     628                 :          0 :   if ( !layer )
     629                 :          0 :     return QgsFeature();
     630                 :            : 
     631                 :          0 :   if ( !layer->isEditable() )
     632                 :          0 :     return QgsFeature();
     633                 :            : 
     634                 :            :   //get context from layer
     635                 :          0 :   QgsExpressionContext context = layer->createExpressionContext();
     636                 :          0 :   context.setFeature( feature );
     637                 :            : 
     638                 :          0 :   QgsFeature newFeature = createFeature( layer, feature.geometry(), feature.attributes().toMap(), &context );
     639                 :          0 :   layer->addFeature( newFeature );
     640                 :            : 
     641                 :          0 :   const QList<QgsRelation> relations = project->relationManager()->referencedRelations( layer );
     642                 :            : 
     643                 :          0 :   const int effectiveMaxDepth = maxDepth > 0 ? maxDepth : 100;
     644                 :            : 
     645                 :          0 :   for ( const QgsRelation &relation : relations )
     646                 :            :   {
     647                 :            :     //check if composition (and not association)
     648                 :          0 :     if ( relation.strength() == QgsRelation::Composition && !referencedLayersBranch.contains( relation.referencedLayer() ) && depth < effectiveMaxDepth )
     649                 :            :     {
     650                 :          0 :       depth++;
     651                 :          0 :       referencedLayersBranch << layer;
     652                 :            : 
     653                 :            :       //get features connected over this relation
     654                 :          0 :       QgsFeatureIterator relatedFeaturesIt = relation.getRelatedFeatures( feature );
     655                 :          0 :       QgsFeatureIds childFeatureIds;
     656                 :          0 :       QgsFeature childFeature;
     657                 :          0 :       while ( relatedFeaturesIt.nextFeature( childFeature ) )
     658                 :            :       {
     659                 :            :         //set childlayer editable
     660                 :          0 :         relation.referencingLayer()->startEditing();
     661                 :            :         //change the fk of the child to the id of the new parent
     662                 :          0 :         const auto pairs = relation.fieldPairs();
     663                 :          0 :         for ( const QgsRelation::FieldPair &fieldPair : pairs )
     664                 :            :         {
     665                 :          0 :           childFeature.setAttribute( fieldPair.first, newFeature.attribute( fieldPair.second ) );
     666                 :            :         }
     667                 :            :         //call the function for the child
     668                 :          0 :         childFeatureIds.insert( duplicateFeature( relation.referencingLayer(), childFeature, project, duplicateFeatureContext, maxDepth, depth, referencedLayersBranch ).id() );
     669                 :          0 :       }
     670                 :            : 
     671                 :            :       //store for feedback
     672                 :          0 :       duplicateFeatureContext.setDuplicatedFeatures( relation.referencingLayer(), childFeatureIds );
     673                 :          0 :     }
     674                 :            :   }
     675                 :            : 
     676                 :            : 
     677                 :          0 :   return newFeature;
     678                 :          0 : }
     679                 :            : 
     680                 :          0 : std::unique_ptr<QgsVectorLayerFeatureSource> QgsVectorLayerUtils::getFeatureSource( QPointer<QgsVectorLayer> layer, QgsFeedback *feedback )
     681                 :            : {
     682                 :          0 :   std::unique_ptr<QgsVectorLayerFeatureSource> featureSource;
     683                 :            : 
     684                 :          0 :   auto getFeatureSource = [ layer, &featureSource, feedback ]
     685                 :            :   {
     686                 :            :     Q_ASSERT( QThread::currentThread() == qApp->thread() || feedback );
     687                 :          0 :     QgsVectorLayer *lyr = layer.data();
     688                 :            : 
     689                 :          0 :     if ( lyr )
     690                 :            :     {
     691                 :          0 :       featureSource.reset( new QgsVectorLayerFeatureSource( lyr ) );
     692                 :          0 :     }
     693                 :          0 :   };
     694                 :            : 
     695                 :          0 :   QgsThreadingUtils::runOnMainThread( getFeatureSource, feedback );
     696                 :            : 
     697                 :          0 :   return featureSource;
     698                 :          0 : }
     699                 :            : 
     700                 :          1 : void QgsVectorLayerUtils::matchAttributesToFields( QgsFeature &feature, const QgsFields &fields )
     701                 :            : {
     702                 :          1 :   if ( !feature.fields().isEmpty() )
     703                 :            :   {
     704                 :          0 :     QgsAttributes attributes;
     705                 :          0 :     attributes.reserve( fields.size() );
     706                 :            :     // feature has a field mapping, so we can match attributes to field names
     707                 :          0 :     for ( const QgsField &field : fields )
     708                 :            :     {
     709                 :          0 :       int index = feature.fields().lookupField( field.name() );
     710                 :          0 :       attributes.append( index >= 0 ? feature.attribute( index ) : QVariant( field.type() ) );
     711                 :            :     }
     712                 :          0 :     feature.setAttributes( attributes );
     713                 :          0 :   }
     714                 :            :   else
     715                 :            :   {
     716                 :            :     // no field name mapping in feature, just use order
     717                 :          1 :     const int lengthDiff = feature.attributes().count() - fields.count();
     718                 :          1 :     if ( lengthDiff > 0 )
     719                 :            :     {
     720                 :            :       // truncate extra attributes
     721                 :          0 :       QgsAttributes attributes = feature.attributes().mid( 0, fields.count() );
     722                 :          0 :       feature.setAttributes( attributes );
     723                 :          0 :     }
     724                 :          1 :     else if ( lengthDiff < 0 )
     725                 :            :     {
     726                 :            :       // add missing null attributes
     727                 :          0 :       QgsAttributes attributes = feature.attributes();
     728                 :          0 :       attributes.reserve( fields.count() );
     729                 :          0 :       for ( int i = feature.attributes().count(); i < fields.count(); ++i )
     730                 :            :       {
     731                 :          0 :         attributes.append( QVariant( fields.at( i ).type() ) );
     732                 :          0 :       }
     733                 :          0 :       feature.setAttributes( attributes );
     734                 :          0 :     }
     735                 :            :   }
     736                 :          1 :   feature.setFields( fields );
     737                 :          1 : }
     738                 :            : 
     739                 :          1 : QgsFeatureList QgsVectorLayerUtils::makeFeatureCompatible( const QgsFeature &feature, const QgsVectorLayer *layer, QgsFeatureSink::SinkFlags sinkFlags )
     740                 :            : {
     741                 :          1 :   QgsWkbTypes::Type inputWkbType( layer->wkbType( ) );
     742                 :          1 :   QgsFeatureList resultFeatures;
     743                 :          1 :   QgsFeature newF( feature );
     744                 :            :   // Fix attributes
     745                 :          1 :   QgsVectorLayerUtils::matchAttributesToFields( newF, layer->fields( ) );
     746                 :            : 
     747                 :          1 :   if ( sinkFlags & QgsFeatureSink::RegeneratePrimaryKey )
     748                 :            :   {
     749                 :            :     // drop incoming primary key values, let them be regenerated
     750                 :          0 :     const QgsAttributeList pkIndexes = layer->dataProvider()->pkAttributeIndexes();
     751                 :          0 :     for ( int index : pkIndexes )
     752                 :            :     {
     753                 :          0 :       if ( index >= 0 )
     754                 :          0 :         newF.setAttribute( index, QVariant() );
     755                 :            :     }
     756                 :          0 :   }
     757                 :            : 
     758                 :            :   // Does geometry need transformations?
     759                 :          1 :   QgsWkbTypes::GeometryType newFGeomType( QgsWkbTypes::geometryType( newF.geometry().wkbType() ) );
     760                 :          2 :   bool newFHasGeom = newFGeomType !=
     761                 :          1 :                      QgsWkbTypes::GeometryType::UnknownGeometry &&
     762                 :          1 :                      newFGeomType != QgsWkbTypes::GeometryType::NullGeometry;
     763                 :          2 :   bool layerHasGeom = inputWkbType !=
     764                 :          1 :                       QgsWkbTypes::Type::NoGeometry &&
     765                 :          1 :                       inputWkbType != QgsWkbTypes::Type::Unknown;
     766                 :            :   // Drop geometry if layer is geometry-less
     767                 :          1 :   if ( ( newFHasGeom && !layerHasGeom ) || !newFHasGeom )
     768                 :            :   {
     769                 :          0 :     QgsFeature _f = QgsFeature( layer->fields() );
     770                 :          0 :     _f.setAttributes( newF.attributes() );
     771                 :          0 :     resultFeatures.append( _f );
     772                 :          0 :   }
     773                 :            :   else
     774                 :            :   {
     775                 :            :     // Geometry need fixing?
     776                 :          1 :     const QVector< QgsGeometry > geometries = newF.geometry().coerceToType( inputWkbType );
     777                 :            : 
     778                 :          1 :     if ( geometries.count() != 1 )
     779                 :            :     {
     780                 :          0 :       QgsAttributeMap attrMap;
     781                 :          0 :       for ( int j = 0; j < newF.fields().count(); j++ )
     782                 :            :       {
     783                 :          0 :         attrMap[j] = newF.attribute( j );
     784                 :          0 :       }
     785                 :          0 :       resultFeatures.reserve( geometries.size() );
     786                 :          0 :       for ( const QgsGeometry &geometry : geometries )
     787                 :            :       {
     788                 :          0 :         QgsFeature _f( createFeature( layer, geometry, attrMap ) );
     789                 :          0 :         resultFeatures.append( _f );
     790                 :          0 :       }
     791                 :          0 :     }
     792                 :            :     else
     793                 :            :     {
     794                 :          1 :       newF.setGeometry( geometries.at( 0 ) );
     795                 :          1 :       resultFeatures.append( newF );
     796                 :            :     }
     797                 :          1 :   }
     798                 :          1 :   return resultFeatures;
     799                 :          1 : }
     800                 :            : 
     801                 :          0 : QgsFeatureList QgsVectorLayerUtils::makeFeaturesCompatible( const QgsFeatureList &features, const QgsVectorLayer *layer, QgsFeatureSink::SinkFlags sinkFlags )
     802                 :            : {
     803                 :          0 :   QgsFeatureList resultFeatures;
     804                 :          0 :   for ( const QgsFeature &f : features )
     805                 :            :   {
     806                 :          0 :     const QgsFeatureList features( makeFeatureCompatible( f, layer, sinkFlags ) );
     807                 :          0 :     for ( const auto &_f : features )
     808                 :            :     {
     809                 :          0 :       resultFeatures.append( _f );
     810                 :            :     }
     811                 :          0 :   }
     812                 :          0 :   return resultFeatures;
     813                 :          0 : }
     814                 :            : 
     815                 :          0 : QList<QgsVectorLayer *> QgsVectorLayerUtils::QgsDuplicateFeatureContext::layers() const
     816                 :            : {
     817                 :          0 :   QList<QgsVectorLayer *> layers;
     818                 :          0 :   QMap<QgsVectorLayer *, QgsFeatureIds>::const_iterator i;
     819                 :          0 :   for ( i = mDuplicatedFeatures.begin(); i != mDuplicatedFeatures.end(); ++i )
     820                 :          0 :     layers.append( i.key() );
     821                 :          0 :   return layers;
     822                 :          0 : }
     823                 :            : 
     824                 :          0 : QgsFeatureIds QgsVectorLayerUtils::QgsDuplicateFeatureContext::duplicatedFeatures( QgsVectorLayer *layer ) const
     825                 :            : {
     826                 :          0 :   return mDuplicatedFeatures[layer];
     827                 :            : }
     828                 :            : 
     829                 :          0 : void QgsVectorLayerUtils::QgsDuplicateFeatureContext::setDuplicatedFeatures( QgsVectorLayer *layer, const QgsFeatureIds &ids )
     830                 :            : {
     831                 :          0 :   if ( mDuplicatedFeatures.contains( layer ) )
     832                 :          0 :     mDuplicatedFeatures[layer] += ids;
     833                 :            :   else
     834                 :          0 :     mDuplicatedFeatures.insert( layer, ids );
     835                 :          0 : }
     836                 :            : /*
     837                 :            : QMap<QgsVectorLayer *, QgsFeatureIds>  QgsVectorLayerUtils::QgsDuplicateFeatureContext::duplicateFeatureContext() const
     838                 :            : {
     839                 :            :   return mDuplicatedFeatures;
     840                 :            : }
     841                 :            : */
     842                 :            : 
     843                 :          1 : QgsVectorLayerUtils::QgsFeatureData::QgsFeatureData( const QgsGeometry &geometry, const QgsAttributeMap &attributes ):
     844                 :          1 :   mGeometry( geometry ),
     845                 :          1 :   mAttributes( attributes )
     846                 :          1 : {}
     847                 :            : 
     848                 :          1 : QgsGeometry QgsVectorLayerUtils::QgsFeatureData::geometry() const
     849                 :            : {
     850                 :          1 :   return mGeometry;
     851                 :            : }
     852                 :            : 
     853                 :          0 : QgsAttributeMap QgsVectorLayerUtils::QgsFeatureData::attributes() const
     854                 :            : {
     855                 :          0 :   return mAttributes;
     856                 :            : }
     857                 :            : 
     858                 :          0 : bool _fieldIsEditable( const QgsVectorLayer *layer, int fieldIndex, const QgsFeature &feature )
     859                 :            : {
     860                 :          0 :   return layer->isEditable() &&
     861                 :          0 :          !layer->editFormConfig().readOnly( fieldIndex ) &&
     862                 :            :          // Provider permissions
     863                 :          0 :          layer->dataProvider() &&
     864                 :          0 :          ( ( layer->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues ) ||
     865                 :          0 :            ( layer->dataProvider()->capabilities() & QgsVectorDataProvider::AddFeatures  && ( FID_IS_NULL( feature.id() ) || FID_IS_NEW( feature.id() ) ) ) )  &&
     866                 :            :          // Field must not be read only
     867                 :          0 :          !layer->fields().at( fieldIndex ).isReadOnly();
     868                 :          0 : }
     869                 :            : 
     870                 :          0 : bool QgsVectorLayerUtils::fieldIsReadOnly( const QgsVectorLayer *layer, int fieldIndex )
     871                 :            : {
     872                 :          0 :   if ( layer->fields().fieldOrigin( fieldIndex ) == QgsFields::OriginJoin )
     873                 :            :   {
     874                 :            :     int srcFieldIndex;
     875                 :          0 :     const QgsVectorLayerJoinInfo *info = layer->joinBuffer()->joinForFieldIndex( fieldIndex, layer->fields(), srcFieldIndex );
     876                 :            : 
     877                 :          0 :     if ( !info || !info->isEditable() || !info->joinLayer() )
     878                 :          0 :       return true;
     879                 :            : 
     880                 :          0 :     return fieldIsReadOnly( info->joinLayer(), srcFieldIndex );
     881                 :            :   }
     882                 :            :   else
     883                 :            :   {
     884                 :            :     // any of these properties makes the field read only
     885                 :          0 :     if ( !layer->isEditable() ||
     886                 :          0 :          layer->editFormConfig().readOnly( fieldIndex ) ||
     887                 :          0 :          !layer->dataProvider() ||
     888                 :          0 :          ( !( layer->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues )
     889                 :          0 :            && !( layer->dataProvider()->capabilities() & QgsVectorDataProvider::AddFeatures ) ) ||
     890                 :          0 :          layer->fields().at( fieldIndex ).isReadOnly() )
     891                 :          0 :       return true;
     892                 :            : 
     893                 :          0 :     return false;
     894                 :            :   }
     895                 :          0 : }
     896                 :            : 
     897                 :          0 : bool QgsVectorLayerUtils::fieldEditabilityDependsOnFeature( const QgsVectorLayer *layer, int fieldIndex )
     898                 :            : {
     899                 :            :   // editability will vary feature-by-feature only for joined fields
     900                 :          0 :   if ( layer->fields().fieldOrigin( fieldIndex ) == QgsFields::OriginJoin )
     901                 :            :   {
     902                 :            :     int srcFieldIndex;
     903                 :          0 :     const QgsVectorLayerJoinInfo *info = layer->joinBuffer()->joinForFieldIndex( fieldIndex, layer->fields(), srcFieldIndex );
     904                 :            : 
     905                 :          0 :     if ( !info || !info->isEditable() || info->hasUpsertOnEdit() )
     906                 :          0 :       return false;
     907                 :            : 
     908                 :            :     // join does not have upsert capabilities, so the ability to edit the joined field will
     909                 :            :     // vary feature-by-feature, depending on whether the join target feature already exists
     910                 :          0 :     return true;
     911                 :            :   }
     912                 :            :   else
     913                 :            :   {
     914                 :          0 :     return false;
     915                 :            :   }
     916                 :          0 : }
     917                 :            : 
     918                 :          0 : bool QgsVectorLayerUtils::fieldIsEditable( const QgsVectorLayer *layer, int fieldIndex, const QgsFeature &feature )
     919                 :            : {
     920                 :          0 :   if ( layer->fields().fieldOrigin( fieldIndex ) == QgsFields::OriginJoin )
     921                 :            :   {
     922                 :            :     int srcFieldIndex;
     923                 :          0 :     const QgsVectorLayerJoinInfo *info = layer->joinBuffer()->joinForFieldIndex( fieldIndex, layer->fields(), srcFieldIndex );
     924                 :            : 
     925                 :          0 :     if ( !info || !info->isEditable() )
     926                 :          0 :       return false;
     927                 :            : 
     928                 :            :     // check that joined feature exist, else it is not editable
     929                 :          0 :     if ( !info->hasUpsertOnEdit() )
     930                 :            :     {
     931                 :          0 :       const QgsFeature joinedFeature = layer->joinBuffer()->joinedFeatureOf( info, feature );
     932                 :          0 :       if ( !joinedFeature.isValid() )
     933                 :          0 :         return false;
     934                 :          0 :     }
     935                 :            : 
     936                 :          0 :     return _fieldIsEditable( info->joinLayer(), srcFieldIndex, feature );
     937                 :            :   }
     938                 :            :   else
     939                 :          0 :     return _fieldIsEditable( layer, fieldIndex, feature );
     940                 :          0 : }
     941                 :            : 
     942                 :            : 
     943                 :          0 : QHash<QString, QHash<QString, QSet<QgsSymbolLayerId>>> QgsVectorLayerUtils::labelMasks( const QgsVectorLayer *layer )
     944                 :            : {
     945                 :          0 :   class LabelMasksVisitor : public QgsStyleEntityVisitorInterface
     946                 :            :   {
     947                 :            :     public:
     948                 :          0 :       bool visitEnter( const QgsStyleEntityVisitorInterface::Node &node ) override
     949                 :            :       {
     950                 :          0 :         if ( node.type == QgsStyleEntityVisitorInterface::NodeType::SymbolRule )
     951                 :            :         {
     952                 :          0 :           currentRule = node.identifier;
     953                 :          0 :           return true;
     954                 :            :         }
     955                 :          0 :         return false;
     956                 :          0 :       }
     957                 :          0 :       bool visit( const QgsStyleEntityVisitorInterface::StyleLeaf &leaf ) override
     958                 :            :       {
     959                 :          0 :         if ( leaf.entity && leaf.entity->type() == QgsStyle::LabelSettingsEntity )
     960                 :            :         {
     961                 :          0 :           auto labelSettingsEntity = static_cast<const QgsStyleLabelSettingsEntity *>( leaf.entity );
     962                 :          0 :           if ( labelSettingsEntity->settings().format().mask().enabled() )
     963                 :            :           {
     964                 :          0 :             for ( const auto &r : labelSettingsEntity->settings().format().mask().maskedSymbolLayers() )
     965                 :            :             {
     966                 :          0 :               masks[currentRule][r.layerId()].insert( r.symbolLayerId() );
     967                 :            :             }
     968                 :          0 :           }
     969                 :          0 :         }
     970                 :          0 :         return true;
     971                 :          0 :       }
     972                 :            : 
     973                 :            :       QHash<QString, QHash<QString, QSet<QgsSymbolLayerId>>> masks;
     974                 :            :       // Current label rule, empty string for a simple labeling
     975                 :            :       QString currentRule;
     976                 :            :   };
     977                 :            : 
     978                 :          0 :   if ( ! layer->labeling() )
     979                 :          0 :     return {};
     980                 :            : 
     981                 :          0 :   LabelMasksVisitor visitor;
     982                 :          0 :   layer->labeling()->accept( &visitor );
     983                 :          0 :   return std::move( visitor.masks );
     984                 :          0 : }
     985                 :            : 
     986                 :          0 : QHash<QString, QSet<QgsSymbolLayerId>> QgsVectorLayerUtils::symbolLayerMasks( const QgsVectorLayer *layer )
     987                 :            : {
     988                 :          0 :   if ( ! layer->renderer() )
     989                 :          0 :     return {};
     990                 :            : 
     991                 :          0 :   class SymbolLayerVisitor : public QgsStyleEntityVisitorInterface
     992                 :            :   {
     993                 :            :     public:
     994                 :          0 :       bool visitEnter( const QgsStyleEntityVisitorInterface::Node &node ) override
     995                 :            :       {
     996                 :          0 :         return ( node.type == QgsStyleEntityVisitorInterface::NodeType::SymbolRule );
     997                 :            :       }
     998                 :            : 
     999                 :          0 :       void visitSymbol( const QgsSymbol *symbol )
    1000                 :            :       {
    1001                 :          0 :         for ( int idx = 0; idx < symbol->symbolLayerCount(); idx++ )
    1002                 :            :         {
    1003                 :          0 :           const QgsSymbolLayer *sl = symbol->symbolLayer( idx );
    1004                 :          0 :           for ( const auto &mask : sl->masks() )
    1005                 :            :           {
    1006                 :          0 :             masks[mask.layerId()].insert( mask.symbolLayerId() );
    1007                 :            :           }
    1008                 :            :           // recurse over sub symbols
    1009                 :          0 :           const QgsSymbol *subSymbol = const_cast<QgsSymbolLayer *>( sl )->subSymbol();
    1010                 :          0 :           if ( subSymbol )
    1011                 :          0 :             visitSymbol( subSymbol );
    1012                 :          0 :         }
    1013                 :          0 :       }
    1014                 :            : 
    1015                 :          0 :       bool visit( const QgsStyleEntityVisitorInterface::StyleLeaf &leaf ) override
    1016                 :            :       {
    1017                 :          0 :         if ( leaf.entity && leaf.entity->type() == QgsStyle::SymbolEntity )
    1018                 :            :         {
    1019                 :          0 :           auto symbolEntity = static_cast<const QgsStyleSymbolEntity *>( leaf.entity );
    1020                 :          0 :           if ( symbolEntity->symbol() )
    1021                 :          0 :             visitSymbol( symbolEntity->symbol() );
    1022                 :          0 :         }
    1023                 :          0 :         return true;
    1024                 :            :       }
    1025                 :            :       QHash<QString, QSet<QgsSymbolLayerId>> masks;
    1026                 :            :   };
    1027                 :            : 
    1028                 :          0 :   SymbolLayerVisitor visitor;
    1029                 :          0 :   layer->renderer()->accept( &visitor );
    1030                 :          0 :   return visitor.masks;
    1031                 :          0 : }
    1032                 :            : 
    1033                 :          0 : QString QgsVectorLayerUtils::getFeatureDisplayString( const QgsVectorLayer *layer, const QgsFeature &feature )
    1034                 :            : {
    1035                 :          0 :   QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
    1036                 :            : 
    1037                 :          0 :   QgsExpression exp( layer->displayExpression() );
    1038                 :          0 :   context.setFeature( feature );
    1039                 :          0 :   exp.prepare( &context );
    1040                 :          0 :   QString displayString = exp.evaluate( &context ).toString();
    1041                 :            : 
    1042                 :          0 :   return displayString;
    1043                 :          0 : }
    1044                 :            : 
    1045                 :          0 : bool QgsVectorLayerUtils::impactsCascadeFeatures( const QgsVectorLayer *layer, const QgsFeatureIds &fids, const QgsProject *project, QgsDuplicateFeatureContext &context, CascadedFeatureFlags flags )
    1046                 :            : {
    1047                 :          0 :   if ( !layer )
    1048                 :          0 :     return false;
    1049                 :            : 
    1050                 :          0 :   const QList<QgsRelation> relations = project->relationManager()->referencedRelations( layer );
    1051                 :          0 :   for ( const QgsRelation &relation : relations )
    1052                 :            :   {
    1053                 :          0 :     if ( relation.strength() == QgsRelation::Composition )
    1054                 :            :     {
    1055                 :          0 :       QgsFeatureIds childFeatureIds;
    1056                 :            : 
    1057                 :          0 :       const auto constFids = fids;
    1058                 :          0 :       for ( const QgsFeatureId fid : constFids )
    1059                 :            :       {
    1060                 :            :         //get features connected over this relation
    1061                 :          0 :         QgsFeatureIterator relatedFeaturesIt = relation.getRelatedFeatures( layer->getFeature( fid ) );
    1062                 :          0 :         QgsFeature childFeature;
    1063                 :          0 :         while ( relatedFeaturesIt.nextFeature( childFeature ) )
    1064                 :            :         {
    1065                 :          0 :           childFeatureIds.insert( childFeature.id() );
    1066                 :            :         }
    1067                 :          0 :       }
    1068                 :            : 
    1069                 :          0 :       if ( childFeatureIds.count() > 0 )
    1070                 :            :       {
    1071                 :          0 :         if ( context.layers().contains( relation.referencingLayer() ) )
    1072                 :            :         {
    1073                 :          0 :           QgsFeatureIds handledFeatureIds = context.duplicatedFeatures( relation.referencingLayer() );
    1074                 :            :           // add feature ids
    1075                 :          0 :           handledFeatureIds.unite( childFeatureIds );
    1076                 :          0 :           context.setDuplicatedFeatures( relation.referencingLayer(), handledFeatureIds );
    1077                 :          0 :         }
    1078                 :            :         else
    1079                 :            :         {
    1080                 :            :           // add layer and feature id
    1081                 :          0 :           context.setDuplicatedFeatures( relation.referencingLayer(), childFeatureIds );
    1082                 :            :         }
    1083                 :          0 :       }
    1084                 :          0 :     }
    1085                 :            :   }
    1086                 :            : 
    1087                 :          0 :   if ( layer->joinBuffer()->containsJoins() )
    1088                 :            :   {
    1089                 :          0 :     const QgsVectorJoinList joins = layer->joinBuffer()->vectorJoins();
    1090                 :          0 :     for ( const QgsVectorLayerJoinInfo &info : joins )
    1091                 :            :     {
    1092                 :          0 :       if ( qobject_cast< QgsAuxiliaryLayer * >( info.joinLayer() ) && flags & IgnoreAuxiliaryLayers )
    1093                 :          0 :         continue;
    1094                 :            : 
    1095                 :          0 :       if ( info.isEditable() && info.hasCascadedDelete() )
    1096                 :            :       {
    1097                 :          0 :         QgsFeatureIds joinFeatureIds;
    1098                 :          0 :         const auto constFids = fids;
    1099                 :          0 :         for ( const QgsFeatureId &fid : constFids )
    1100                 :            :         {
    1101                 :          0 :           const QgsFeature joinFeature = layer->joinBuffer()->joinedFeatureOf( &info, layer->getFeature( fid ) );
    1102                 :          0 :           if ( joinFeature.isValid() )
    1103                 :          0 :             joinFeatureIds.insert( joinFeature.id() );
    1104                 :          0 :         }
    1105                 :            : 
    1106                 :          0 :         if ( joinFeatureIds.count() > 0 )
    1107                 :            :         {
    1108                 :          0 :           if ( context.layers().contains( info.joinLayer() ) )
    1109                 :            :           {
    1110                 :          0 :             QgsFeatureIds handledFeatureIds = context.duplicatedFeatures( info.joinLayer() );
    1111                 :            :             // add feature ids
    1112                 :          0 :             handledFeatureIds.unite( joinFeatureIds );
    1113                 :          0 :             context.setDuplicatedFeatures( info.joinLayer(), handledFeatureIds );
    1114                 :          0 :           }
    1115                 :            :           else
    1116                 :            :           {
    1117                 :            :             // add layer and feature id
    1118                 :          0 :             context.setDuplicatedFeatures( info.joinLayer(), joinFeatureIds );
    1119                 :            :           }
    1120                 :          0 :         }
    1121                 :          0 :       }
    1122                 :            :     }
    1123                 :          0 :   }
    1124                 :            : 
    1125                 :          0 :   return !context.layers().isEmpty();
    1126                 :          0 : }
    1127                 :            : 
    1128                 :          0 : QString QgsVectorLayerUtils::guessFriendlyIdentifierField( const QgsFields &fields )
    1129                 :            : {
    1130                 :          0 :   if ( fields.isEmpty() )
    1131                 :          0 :     return QString();
    1132                 :            : 
    1133                 :            :   // Check the fields and keep the first one that matches.
    1134                 :            :   // We assume that the user has organized the data with the
    1135                 :            :   // more "interesting" field names first. As such, name should
    1136                 :            :   // be selected before oldname, othername, etc.
    1137                 :            :   // This candidates list is a prioritized list of candidates ranked by "interestingness"!
    1138                 :            :   // See discussion at https://github.com/qgis/QGIS/pull/30245 - this list must NOT be translated,
    1139                 :            :   // but adding hardcoded localized variants of the strings is encouraged.
    1140                 :          0 :   static QStringList sCandidates{ QStringLiteral( "name" ),
    1141                 :          0 :                                   QStringLiteral( "title" ),
    1142                 :          0 :                                   QStringLiteral( "heibt" ),
    1143                 :          0 :                                   QStringLiteral( "desc" ),
    1144                 :          0 :                                   QStringLiteral( "nom" ),
    1145                 :          0 :                                   QStringLiteral( "street" ),
    1146                 :          0 :                                   QStringLiteral( "road" ) };
    1147                 :            : 
    1148                 :            :   // anti-names
    1149                 :            :   // this list of strings indicates parts of field names which make the name "less interesting".
    1150                 :            :   // For instance, we'd normally like to default to a field called "name" or "id", but if instead we
    1151                 :            :   // find one called "typename" or "typeid", then that's most likely a classification of the feature and not the
    1152                 :            :   // best choice to default to
    1153                 :          0 :   static QStringList sAntiCandidates{ QStringLiteral( "type" ),
    1154                 :          0 :                                       QStringLiteral( "class" ),
    1155                 :          0 :                                       QStringLiteral( "cat" )
    1156                 :            :                                     };
    1157                 :            : 
    1158                 :          0 :   QString bestCandidateName;
    1159                 :          0 :   QString bestCandidateNameWithAntiCandidate;
    1160                 :            : 
    1161                 :          0 :   for ( const QString &candidate : sCandidates )
    1162                 :            :   {
    1163                 :          0 :     for ( const QgsField &field : fields )
    1164                 :            :     {
    1165                 :          0 :       const QString fldName = field.name();
    1166                 :          0 :       if ( fldName.contains( candidate, Qt::CaseInsensitive ) )
    1167                 :            :       {
    1168                 :          0 :         bool isAntiCandidate = false;
    1169                 :          0 :         for ( const QString &antiCandidate : sAntiCandidates )
    1170                 :            :         {
    1171                 :          0 :           if ( fldName.contains( antiCandidate, Qt::CaseInsensitive ) )
    1172                 :            :           {
    1173                 :          0 :             isAntiCandidate = true;
    1174                 :          0 :             break;
    1175                 :            :           }
    1176                 :            :         }
    1177                 :            : 
    1178                 :          0 :         if ( isAntiCandidate )
    1179                 :            :         {
    1180                 :          0 :           if ( bestCandidateNameWithAntiCandidate.isEmpty() )
    1181                 :            :           {
    1182                 :          0 :             bestCandidateNameWithAntiCandidate = fldName;
    1183                 :          0 :           }
    1184                 :          0 :         }
    1185                 :            :         else
    1186                 :            :         {
    1187                 :          0 :           bestCandidateName = fldName;
    1188                 :          0 :           break;
    1189                 :            :         }
    1190                 :          0 :       }
    1191                 :          0 :     }
    1192                 :            : 
    1193                 :          0 :     if ( !bestCandidateName.isEmpty() )
    1194                 :          0 :       break;
    1195                 :            :   }
    1196                 :            : 
    1197                 :          0 :   const QString candidateName = bestCandidateName.isEmpty() ? bestCandidateNameWithAntiCandidate : bestCandidateName;
    1198                 :          0 :   if ( !candidateName.isEmpty() )
    1199                 :            :   {
    1200                 :          0 :     return candidateName;
    1201                 :            :   }
    1202                 :            :   else
    1203                 :            :   {
    1204                 :            :     // no good matches found by name, so scan through and look for the first string field
    1205                 :          0 :     for ( const QgsField &field : fields )
    1206                 :            :     {
    1207                 :          0 :       if ( field.type() == QVariant::String )
    1208                 :          0 :         return field.name();
    1209                 :            :     }
    1210                 :            : 
    1211                 :            :     // no string fields found - just return first field
    1212                 :          0 :     return fields.at( 0 ).name();
    1213                 :            :   }
    1214                 :          0 : }

Generated by: LCOV version 1.14