Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsvaluerelationfieldformatter.cpp - QgsValueRelationFieldFormatter 3 : : 4 : : --------------------- 5 : : begin : 3.12.2016 6 : : copyright : (C) 2016 by Matthias Kuhn 7 : : email : matthias@opengis.ch 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 : : #include "qgsvaluerelationfieldformatter.h" 17 : : 18 : : #include "qgis.h" 19 : : #include "qgsproject.h" 20 : : #include "qgsvectorlayer.h" 21 : : #include "qgsexpressionnodeimpl.h" 22 : : #include "qgsapplication.h" 23 : : #include "qgsexpressioncontextutils.h" 24 : : #include "qgsvectorlayerref.h" 25 : : #include "qgspostgresstringutils.h" 26 : : #include "qgsmessagelog.h" 27 : : 28 : : #include <nlohmann/json.hpp> 29 : : using namespace nlohmann; 30 : : 31 : : #include <QSettings> 32 : : 33 : 0 : bool orderByKeyLessThan( const QgsValueRelationFieldFormatter::ValueRelationItem &p1, const QgsValueRelationFieldFormatter::ValueRelationItem &p2 ) 34 : : { 35 : 0 : return qgsVariantLessThan( p1.key, p2.key ); 36 : : } 37 : : 38 : 0 : bool orderByValueLessThan( const QgsValueRelationFieldFormatter::ValueRelationItem &p1, const QgsValueRelationFieldFormatter::ValueRelationItem &p2 ) 39 : : { 40 : 0 : return qgsVariantLessThan( p1.value, p2.value ); 41 : 0 : } 42 : : 43 : 5 : QgsValueRelationFieldFormatter::QgsValueRelationFieldFormatter() 44 : 10 : { 45 : 5 : setFlags( flags() | QgsFieldFormatter::CanProvideAvailableValues ); 46 : 5 : } 47 : : 48 : 5 : QString QgsValueRelationFieldFormatter::id() const 49 : : { 50 : 10 : return QStringLiteral( "ValueRelation" ); 51 : : } 52 : : 53 : 0 : QString QgsValueRelationFieldFormatter::representValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const 54 : : { 55 : 0 : ValueRelationCache vrCache; 56 : : 57 : 0 : if ( cache.isValid() ) 58 : : { 59 : 0 : vrCache = cache.value<QgsValueRelationFieldFormatter::ValueRelationCache>(); 60 : 0 : } 61 : : else 62 : : { 63 : 0 : vrCache = QgsValueRelationFieldFormatter::createCache( config ); 64 : : } 65 : : 66 : 0 : if ( config.value( QStringLiteral( "AllowMulti" ) ).toBool() ) 67 : : { 68 : 0 : QStringList keyList; 69 : : 70 : 0 : if ( layer->fields().at( fieldIndex ).type() == QVariant::Map ) 71 : : { 72 : : //because of json it's stored as QVariantList 73 : 0 : keyList = value.toStringList(); 74 : 0 : } 75 : : else 76 : : { 77 : 0 : keyList = valueToStringList( value ); 78 : : } 79 : : 80 : 0 : QStringList valueList; 81 : : 82 : 0 : for ( const QgsValueRelationFieldFormatter::ValueRelationItem &item : std::as_const( vrCache ) ) 83 : : { 84 : 0 : if ( keyList.contains( item.key.toString() ) ) 85 : : { 86 : 0 : valueList << item.value; 87 : 0 : } 88 : : } 89 : : 90 : 0 : return valueList.join( QLatin1String( ", " ) ).prepend( '{' ).append( '}' ); 91 : 0 : } 92 : : else 93 : : { 94 : 0 : if ( value.isNull() ) 95 : : { 96 : 0 : return QgsApplication::nullRepresentation(); 97 : : } 98 : : 99 : 0 : for ( const QgsValueRelationFieldFormatter::ValueRelationItem &item : std::as_const( vrCache ) ) 100 : : { 101 : 0 : if ( item.key == value ) 102 : : { 103 : 0 : return item.value; 104 : : } 105 : : } 106 : : } 107 : : 108 : 0 : return QStringLiteral( "(%1)" ).arg( value.toString() ); 109 : 0 : } 110 : : 111 : 0 : QVariant QgsValueRelationFieldFormatter::sortValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const 112 : : { 113 : 0 : return value.isNull() ? QString() : representValue( layer, fieldIndex, config, cache, value ); 114 : 0 : } 115 : : 116 : 0 : QVariant QgsValueRelationFieldFormatter::createCache( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config ) const 117 : : { 118 : : Q_UNUSED( layer ) 119 : : Q_UNUSED( fieldIndex ) 120 : 0 : return QVariant::fromValue<ValueRelationCache>( createCache( config ) ); 121 : : 122 : 0 : } 123 : : 124 : 0 : QgsValueRelationFieldFormatter::ValueRelationCache QgsValueRelationFieldFormatter::createCache( 125 : : const QVariantMap &config, 126 : : const QgsFeature &formFeature, 127 : : const QgsFeature &parentFormFeature ) 128 : : { 129 : 0 : ValueRelationCache cache; 130 : : 131 : 0 : const QgsVectorLayer *layer = resolveLayer( config, QgsProject::instance() ); 132 : : 133 : 0 : if ( !layer ) 134 : 0 : return cache; 135 : : 136 : 0 : QgsFields fields = layer->fields(); 137 : 0 : int ki = fields.indexOf( config.value( QStringLiteral( "Key" ) ).toString() ); 138 : 0 : int vi = fields.indexOf( config.value( QStringLiteral( "Value" ) ).toString() ); 139 : : 140 : 0 : QgsFeatureRequest request; 141 : : 142 : 0 : request.setFlags( QgsFeatureRequest::NoGeometry ); 143 : 0 : QgsAttributeIds subsetOfAttributes { ki, vi }; 144 : : 145 : 0 : const QString descriptionExpressionString = config.value( "Description" ).toString(); 146 : 0 : QgsExpression descriptionExpression( descriptionExpressionString ); 147 : 0 : QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) ); 148 : 0 : descriptionExpression.prepare( &context ); 149 : 0 : subsetOfAttributes += descriptionExpression.referencedAttributeIndexes( layer->fields() ); 150 : 0 : request.setSubsetOfAttributes( qgis::setToList( subsetOfAttributes ) ); 151 : : 152 : 0 : const QString filterExpression = config.value( QStringLiteral( "FilterExpression" ) ).toString(); 153 : : 154 : : // Skip the filter and build a full cache if the form scope is required and the feature 155 : : // is not valid or the attributes required for the filter have no valid value 156 : : // Note: parent form scope is not checked for usability because it's supposed to 157 : : // be used into a coalesce that retrieve the current value of the parent 158 : : // from the parent layer when used outside of an embedded form 159 : 0 : if ( ! filterExpression.isEmpty() && ( !( expressionRequiresFormScope( filterExpression ) ) 160 : 0 : || expressionIsUsable( filterExpression, formFeature ) ) ) 161 : : { 162 : 0 : QgsExpressionContext filterContext = context; 163 : 0 : if ( formFeature.isValid( ) && QgsValueRelationFieldFormatter::expressionRequiresFormScope( filterExpression ) ) 164 : 0 : filterContext.appendScope( QgsExpressionContextUtils::formScope( formFeature ) ); 165 : 0 : if ( parentFormFeature.isValid() && QgsValueRelationFieldFormatter::expressionRequiresParentFormScope( filterExpression ) ) 166 : 0 : filterContext.appendScope( QgsExpressionContextUtils::parentFormScope( parentFormFeature ) ); 167 : 0 : request.setExpressionContext( filterContext ); 168 : 0 : request.setFilterExpression( filterExpression ); 169 : 0 : } 170 : : 171 : 0 : QgsFeatureIterator fit = layer->getFeatures( request ); 172 : : 173 : 0 : QgsFeature f; 174 : 0 : while ( fit.nextFeature( f ) ) 175 : : { 176 : 0 : QString description; 177 : 0 : if ( descriptionExpression.isValid() ) 178 : : { 179 : 0 : context.setFeature( f ); 180 : 0 : description = descriptionExpression.evaluate( &context ).toString(); 181 : 0 : } 182 : 0 : cache.append( ValueRelationItem( f.attribute( ki ), f.attribute( vi ).toString(), description ) ); 183 : 0 : } 184 : : 185 : 0 : if ( config.value( QStringLiteral( "OrderByValue" ) ).toBool() ) 186 : : { 187 : 0 : std::sort( cache.begin(), cache.end(), orderByValueLessThan ); 188 : 0 : } 189 : : else 190 : : { 191 : 0 : std::sort( cache.begin(), cache.end(), orderByKeyLessThan ); 192 : : } 193 : : 194 : 0 : return cache; 195 : 0 : } 196 : : 197 : : 198 : 0 : QList<QgsVectorLayerRef> QgsValueRelationFieldFormatter::layerDependencies( const QVariantMap &config ) const 199 : : { 200 : 0 : QList<QgsVectorLayerRef> result; 201 : 0 : const QString layerId { config.value( QStringLiteral( "Layer" ) ).toString() }; 202 : 0 : const QString layerName { config.value( QStringLiteral( "LayerName" ) ).toString() }; 203 : 0 : const QString providerName { config.value( QStringLiteral( "LayerProviderName" ) ).toString() }; 204 : 0 : const QString layerSource { config.value( QStringLiteral( "LayerSource" ) ).toString() }; 205 : 0 : if ( ! layerId.isEmpty() && ! layerName.isEmpty() && ! providerName.isEmpty() && ! layerSource.isEmpty() ) 206 : : { 207 : 0 : result.append( QgsVectorLayerRef( layerId, layerName, layerSource, providerName ) ); 208 : 0 : } 209 : 0 : return result; 210 : 0 : } 211 : : 212 : 0 : QVariantList QgsValueRelationFieldFormatter::availableValues( const QVariantMap &config, int countLimit, const QgsFieldFormatterContext &context ) const 213 : : { 214 : 0 : QVariantList values; 215 : : 216 : 0 : if ( auto *lProject = context.project() ) 217 : : { 218 : 0 : const QgsVectorLayer *referencedLayer = qobject_cast<QgsVectorLayer *>( lProject->mapLayer( config[QStringLiteral( "Layer" )].toString() ) ); 219 : 0 : if ( referencedLayer ) 220 : : { 221 : 0 : int fieldIndex = referencedLayer->fields().indexOf( config.value( QStringLiteral( "Key" ) ).toString() ); 222 : 0 : values = qgis::setToList( referencedLayer->uniqueValues( fieldIndex, countLimit ) ); 223 : 0 : } 224 : 0 : } 225 : 0 : return values; 226 : 0 : } 227 : : 228 : 0 : QStringList QgsValueRelationFieldFormatter::valueToStringList( const QVariant &value ) 229 : : { 230 : 0 : QStringList checkList; 231 : 0 : if ( value.type() == QVariant::StringList ) 232 : : { 233 : 0 : checkList = value.toStringList(); 234 : 0 : } 235 : : else 236 : : { 237 : 0 : QVariantList valuesList; 238 : 0 : if ( value.type() == QVariant::String ) 239 : : { 240 : : // This must be an array representation 241 : 0 : auto newVal { value }; 242 : 0 : if ( newVal.toString().trimmed().startsWith( '{' ) ) 243 : : { 244 : : //normal case 245 : 0 : valuesList = QgsPostgresStringUtils::parseArray( newVal.toString() ); 246 : 0 : } 247 : 0 : else if ( newVal.toString().trimmed().startsWith( '[' ) ) 248 : : { 249 : : //fallback, in case it's a json array 250 : : try 251 : : { 252 : 0 : for ( auto &element : json::parse( newVal.toString().toStdString() ) ) 253 : : { 254 : 0 : if ( element.is_number_integer() ) 255 : : { 256 : 0 : valuesList.push_back( element.get<int>() ); 257 : 0 : } 258 : 0 : else if ( element.is_number_unsigned() ) 259 : : { 260 : 0 : valuesList.push_back( element.get<unsigned>() ); 261 : 0 : } 262 : 0 : else if ( element.is_string() ) 263 : : { 264 : 0 : valuesList.push_back( QString::fromStdString( element.get<std::string>() ) ); 265 : 0 : } 266 : : } 267 : 0 : } 268 : : catch ( json::parse_error &ex ) 269 : : { 270 : 0 : QgsMessageLog::logMessage( QObject::tr( "Cannot parse JSON like string '%1' Error: %2" ).arg( newVal.toString(), ex.what() ) ); 271 : 0 : } 272 : 0 : } 273 : 0 : } 274 : 0 : else if ( value.type() == QVariant::List ) 275 : : { 276 : 0 : valuesList = value.toList( ); 277 : 0 : } 278 : : 279 : 0 : checkList.reserve( valuesList.size() ); 280 : 0 : for ( const QVariant &listItem : std::as_const( valuesList ) ) 281 : : { 282 : 0 : QString v( listItem.toString( ) ); 283 : 0 : if ( ! v.isEmpty() ) 284 : 0 : checkList.append( v ); 285 : 0 : } 286 : 0 : } 287 : 0 : return checkList; 288 : 0 : } 289 : : 290 : : 291 : 0 : QSet<QString> QgsValueRelationFieldFormatter::expressionFormVariables( const QString &expression ) 292 : : { 293 : 0 : std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::formScope() ); 294 : 0 : QSet< QString > formVariables = qgis::listToSet( scope->variableNames() ); 295 : 0 : const QSet< QString > usedVariables = QgsExpression( expression ).referencedVariables(); 296 : 0 : formVariables.intersect( usedVariables ); 297 : 0 : return formVariables; 298 : 0 : } 299 : : 300 : 0 : QSet<QString> QgsValueRelationFieldFormatter::expressionParentFormVariables( const QString &expression ) 301 : : { 302 : 0 : std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::parentFormScope() ); 303 : 0 : QSet< QString > formVariables = qgis::listToSet( scope->variableNames() ); 304 : 0 : const QSet< QString > usedVariables = QgsExpression( expression ).referencedVariables(); 305 : 0 : formVariables.intersect( usedVariables ); 306 : 0 : return formVariables; 307 : 0 : } 308 : : 309 : 0 : bool QgsValueRelationFieldFormatter::expressionRequiresFormScope( const QString &expression ) 310 : : { 311 : 0 : return !( expressionFormAttributes( expression ).isEmpty() && expressionFormVariables( expression ).isEmpty() ); 312 : 0 : } 313 : : 314 : 0 : bool QgsValueRelationFieldFormatter::expressionRequiresParentFormScope( const QString &expression ) 315 : : { 316 : 0 : return !( expressionParentFormAttributes( expression ).isEmpty() && expressionParentFormVariables( expression ).isEmpty() ); 317 : 0 : } 318 : : 319 : 0 : QSet<QString> QgsValueRelationFieldFormatter::expressionParentFormAttributes( const QString &expression ) 320 : : { 321 : 0 : QSet<QString> attributes; 322 : 0 : QgsExpression exp( expression ); 323 : 0 : std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::parentFormScope() ); 324 : : // List of form function names used in the expression 325 : 0 : const QSet<QString> formFunctions( qgis::listToSet( scope->functionNames() ) 326 : 0 : .intersect( exp.referencedFunctions( ) ) ); 327 : 0 : const QList<const QgsExpressionNodeFunction *> expFunctions( exp.findNodes<QgsExpressionNodeFunction>() ); 328 : 0 : QgsExpressionContext context; 329 : 0 : for ( const auto &f : expFunctions ) 330 : : { 331 : 0 : QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[f->fnIndex()]; 332 : 0 : if ( formFunctions.contains( fd->name( ) ) ) 333 : : { 334 : 0 : for ( const auto ¶m : f->args( )->list() ) 335 : : { 336 : 0 : attributes.insert( param->eval( &exp, &context ).toString() ); 337 : : } 338 : 0 : } 339 : : } 340 : 0 : return attributes; 341 : 0 : } 342 : : 343 : 0 : QSet<QString> QgsValueRelationFieldFormatter::expressionFormAttributes( const QString &expression ) 344 : : { 345 : 0 : QSet<QString> attributes; 346 : 0 : QgsExpression exp( expression ); 347 : 0 : std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::formScope() ); 348 : : // List of form function names used in the expression 349 : 0 : const QSet<QString> formFunctions( qgis::listToSet( scope->functionNames() ) 350 : 0 : .intersect( exp.referencedFunctions( ) ) ); 351 : 0 : const QList<const QgsExpressionNodeFunction *> expFunctions( exp.findNodes<QgsExpressionNodeFunction>() ); 352 : 0 : QgsExpressionContext context; 353 : 0 : for ( const auto &f : expFunctions ) 354 : : { 355 : 0 : QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[f->fnIndex()]; 356 : 0 : if ( formFunctions.contains( fd->name( ) ) ) 357 : : { 358 : 0 : for ( const auto ¶m : f->args( )->list() ) 359 : : { 360 : 0 : attributes.insert( param->eval( &exp, &context ).toString() ); 361 : : } 362 : 0 : } 363 : : } 364 : 0 : return attributes; 365 : 0 : } 366 : : 367 : 0 : bool QgsValueRelationFieldFormatter::expressionIsUsable( const QString &expression, 368 : : const QgsFeature &feature, 369 : : const QgsFeature &parentFeature ) 370 : : { 371 : 0 : const QSet<QString> attrs = expressionFormAttributes( expression ); 372 : 0 : for ( auto it = attrs.constBegin() ; it != attrs.constEnd(); it++ ) 373 : : { 374 : 0 : if ( ! feature.attribute( *it ).isValid() ) 375 : 0 : return false; 376 : 0 : } 377 : : 378 : 0 : if ( ! expressionFormVariables( expression ).isEmpty() && feature.geometry().isEmpty( ) ) 379 : 0 : return false; 380 : : 381 : 0 : if ( parentFeature.isValid() ) 382 : : { 383 : 0 : const QSet<QString> parentAttrs = expressionParentFormAttributes( expression ); 384 : 0 : for ( auto it = parentAttrs.constBegin() ; it != parentAttrs.constEnd(); it++ ) 385 : : { 386 : 0 : if ( ! parentFeature.attribute( *it ).isValid() ) 387 : 0 : return false; 388 : 0 : } 389 : 0 : if ( ! expressionParentFormVariables( expression ).isEmpty() && parentFeature.geometry().isEmpty( ) ) 390 : 0 : return false; 391 : 0 : } 392 : 0 : return true; 393 : 0 : } 394 : : 395 : 0 : QgsVectorLayer *QgsValueRelationFieldFormatter::resolveLayer( const QVariantMap &config, const QgsProject *project ) 396 : : { 397 : 0 : QgsVectorLayerRef ref { config.value( QStringLiteral( "Layer" ) ).toString(), 398 : 0 : config.value( QStringLiteral( "LayerName" ) ).toString(), 399 : 0 : config.value( QStringLiteral( "LayerSource" ) ).toString(), 400 : 0 : config.value( QStringLiteral( "LayerProviderName" ) ).toString() }; 401 : 0 : return ref.resolveByIdOrNameOnly( project ); 402 : 0 : }