QGIS API Documentation 3.27.0-Master (f261cc1f8b)
qgsexpressioncontext.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsexpressioncontext.cpp
3 ------------------------
4 Date : April 2015
5 Copyright : (C) 2015 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
17#include "qgslogger.h"
18#include "qgsxmlutils.h"
19#include "qgsexpression.h"
20
21const QString QgsExpressionContext::EXPR_FIELDS( QStringLiteral( "_fields_" ) );
22const QString QgsExpressionContext::EXPR_ORIGINAL_VALUE( QStringLiteral( "value" ) );
23const QString QgsExpressionContext::EXPR_SYMBOL_COLOR( QStringLiteral( "symbol_color" ) );
24const QString QgsExpressionContext::EXPR_SYMBOL_ANGLE( QStringLiteral( "symbol_angle" ) );
25const QString QgsExpressionContext::EXPR_GEOMETRY_PART_COUNT( QStringLiteral( "geometry_part_count" ) );
26const QString QgsExpressionContext::EXPR_GEOMETRY_PART_NUM( QStringLiteral( "geometry_part_num" ) );
27const QString QgsExpressionContext::EXPR_GEOMETRY_RING_NUM( QStringLiteral( "geometry_ring_num" ) );
28const QString QgsExpressionContext::EXPR_GEOMETRY_POINT_COUNT( QStringLiteral( "geometry_point_count" ) );
29const QString QgsExpressionContext::EXPR_GEOMETRY_POINT_NUM( QStringLiteral( "geometry_point_num" ) );
30const QString QgsExpressionContext::EXPR_CLUSTER_SIZE( QStringLiteral( "cluster_size" ) );
31const QString QgsExpressionContext::EXPR_CLUSTER_COLOR( QStringLiteral( "cluster_color" ) );
32
33//
34// QgsExpressionContextScope
35//
36
38 : mName( name )
39{
40
41}
42
44 : mName( other.mName )
45 , mVariables( other.mVariables )
46 , mHasFeature( other.mHasFeature )
47 , mFeature( other.mFeature )
48 , mHasGeometry( other.mHasGeometry )
49 , mGeometry( other.mGeometry )
50{
51 QHash<QString, QgsScopedExpressionFunction * >::const_iterator it = other.mFunctions.constBegin();
52 for ( ; it != other.mFunctions.constEnd(); ++it )
53 {
54 mFunctions.insert( it.key(), it.value()->clone() );
55 }
56}
57
59{
60 mName = other.mName;
61 mVariables = other.mVariables;
62 mHasFeature = other.mHasFeature;
63 mFeature = other.mFeature;
64 mHasGeometry = other.mHasGeometry;
65 mGeometry = other.mGeometry;
66
67 qDeleteAll( mFunctions );
68 mFunctions.clear();
69 QHash<QString, QgsScopedExpressionFunction * >::const_iterator it = other.mFunctions.constBegin();
70 for ( ; it != other.mFunctions.constEnd(); ++it )
71 {
72 mFunctions.insert( it.key(), it.value()->clone() );
73 }
74
75 return *this;
76}
77
79{
80 qDeleteAll( mFunctions );
81}
82
83void QgsExpressionContextScope::setVariable( const QString &name, const QVariant &value, bool isStatic )
84{
85 auto it = mVariables.find( name );
86 if ( it != mVariables.end() )
87 {
88 it->value = value;
89 it->isStatic = isStatic;
90 }
91 else
92 {
94 }
95}
96
98{
99 mVariables.insert( variable.name, variable );
100}
101
103{
104 return mVariables.remove( name ) > 0;
105}
106
107bool QgsExpressionContextScope::hasVariable( const QString &name ) const
108{
109 return mVariables.contains( name );
110}
111
112QVariant QgsExpressionContextScope::variable( const QString &name ) const
113{
114 return hasVariable( name ) ? mVariables.value( name ).value : QVariant();
115}
116
118{
119 QStringList names = mVariables.keys();
120 return names;
121}
122
124class QgsExpressionContextVariableCompare
125{
126 public:
127 explicit QgsExpressionContextVariableCompare( const QgsExpressionContextScope &scope )
128 : mScope( scope )
129 { }
130
131 bool operator()( const QString &a, const QString &b ) const
132 {
133 bool aReadOnly = mScope.isReadOnly( a );
134 bool bReadOnly = mScope.isReadOnly( b );
135 if ( aReadOnly != bReadOnly )
136 return aReadOnly;
137 return QString::localeAwareCompare( a, b ) < 0;
138 }
139
140 private:
141 const QgsExpressionContextScope &mScope;
142};
144
146{
147 QStringList allVariables = mVariables.keys();
148 QStringList filtered;
149 const auto constAllVariables = allVariables;
150 for ( const QString &variable : constAllVariables )
151 {
152 if ( variable.startsWith( '_' ) )
153 continue;
154
155 filtered << variable;
156 }
157 QgsExpressionContextVariableCompare cmp( *this );
158 std::sort( filtered.begin(), filtered.end(), cmp );
159
160 return filtered;
161}
162
163bool QgsExpressionContextScope::isReadOnly( const QString &name ) const
164{
165 return hasVariable( name ) ? mVariables.value( name ).readOnly : false;
166}
167
168bool QgsExpressionContextScope::isStatic( const QString &name ) const
169{
170 return hasVariable( name ) ? mVariables.value( name ).isStatic : false;
171}
172
173QString QgsExpressionContextScope::description( const QString &name ) const
174{
175 return hasVariable( name ) ? mVariables.value( name ).description : QString();
176}
177
178bool QgsExpressionContextScope::hasFunction( const QString &name ) const
179{
180 return mFunctions.contains( name );
181}
182
184{
185 return mFunctions.contains( name ) ? mFunctions.value( name ) : nullptr;
186}
187
189{
190 return mFunctions.keys();
191}
192
194{
195 mFunctions.insert( name, function );
196}
197
198
200{
201 addVariable( StaticVariable( QgsExpressionContext::EXPR_FIELDS, QVariant::fromValue( fields ), true ) );
202}
203
204void QgsExpressionContextScope::readXml( const QDomElement &element, const QgsReadWriteContext & )
205{
206 const QDomNodeList variablesNodeList = element.childNodes();
207 for ( int i = 0; i < variablesNodeList.size(); ++i )
208 {
209 const QDomElement variableElement = variablesNodeList.at( i ).toElement();
210 const QString key = variableElement.attribute( QStringLiteral( "name" ) );
211 const QVariant value = QgsXmlUtils::readVariant( variableElement.firstChildElement( QStringLiteral( "Option" ) ) );
212 setVariable( key, value );
213 }
214}
215
216bool QgsExpressionContextScope::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext & ) const
217{
218 for ( auto it = mVariables.constBegin(); it != mVariables.constEnd(); ++it )
219 {
220 QDomElement varElem = document.createElement( QStringLiteral( "Variable" ) );
221 varElem.setAttribute( QStringLiteral( "name" ), it.key() );
222 QDomElement valueElem = QgsXmlUtils::writeVariant( it.value().value, document );
223 varElem.appendChild( valueElem );
224 element.appendChild( varElem );
225 }
226 return true;
227}
228
229
230//
231// QgsExpressionContext
232//
233
234QgsExpressionContext::QgsExpressionContext( const QList<QgsExpressionContextScope *> &scopes )
235 : mStack( scopes )
236{
237}
238
240{
241 for ( const QgsExpressionContextScope *scope : std::as_const( other.mStack ) )
242 {
243 mStack << new QgsExpressionContextScope( *scope );
244 }
245 mHighlightedVariables = other.mHighlightedVariables;
246 mHighlightedFunctions = other.mHighlightedFunctions;
247 mCachedValues = other.mCachedValues;
248}
249
251{
252 if ( this != &other )
253 {
254 qDeleteAll( mStack );
255 // move the stack over
256 mStack = other.mStack;
257 other.mStack.clear();
258
259 mHighlightedVariables = other.mHighlightedVariables;
260 mHighlightedFunctions = other.mHighlightedFunctions;
261 mCachedValues = other.mCachedValues;
262 }
263 return *this;
264}
265
267{
268 if ( &other == this )
269 return *this;
270
271 qDeleteAll( mStack );
272 mStack.clear();
273 for ( const QgsExpressionContextScope *scope : std::as_const( other.mStack ) )
274 {
275 mStack << new QgsExpressionContextScope( *scope );
276 }
277 mHighlightedVariables = other.mHighlightedVariables;
278 mHighlightedFunctions = other.mHighlightedFunctions;
279 mCachedValues = other.mCachedValues;
280 return *this;
281}
282
284{
285 qDeleteAll( mStack );
286 mStack.clear();
287}
288
289bool QgsExpressionContext::hasVariable( const QString &name ) const
290{
291 const auto constMStack = mStack;
292 for ( const QgsExpressionContextScope *scope : constMStack )
293 {
294 if ( scope->hasVariable( name ) )
295 return true;
296 }
297 return false;
298}
299
300QVariant QgsExpressionContext::variable( const QString &name ) const
301{
303 return scope ? scope->variable( name ) : QVariant();
304}
305
307{
308 QStringList names = variableNames();
309 QVariantMap m;
310 const auto constNames = names;
311 for ( const QString &name : constNames )
312 {
313 m.insert( name, variable( name ) );
314 }
315 return m;
316}
317
318bool QgsExpressionContext::isHighlightedVariable( const QString &name ) const
319{
320 return mHighlightedVariables.contains( name );
321}
322
324{
325 return mHighlightedVariables;
326}
327
328void QgsExpressionContext::setHighlightedVariables( const QStringList &variableNames )
329{
330 mHighlightedVariables = variableNames;
331}
332
333bool QgsExpressionContext::isHighlightedFunction( const QString &name ) const
334{
335 return mHighlightedFunctions.contains( name );
336}
337
338void QgsExpressionContext::setHighlightedFunctions( const QStringList &names )
339{
340 mHighlightedFunctions = names;
341}
342
344{
345 //iterate through stack backwards, so that higher priority variables take precedence
346 QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
347 while ( it != mStack.constBegin() )
348 {
349 --it;
350 if ( ( *it )->hasVariable( name ) )
351 return ( *it );
352 }
353 return nullptr;
354}
355
357{
358 //iterate through stack backwards, so that higher priority variables take precedence
359 QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
360 while ( it != mStack.constBegin() )
361 {
362 --it;
363 if ( ( *it )->hasVariable( name ) )
364 return ( *it );
365 }
366 return nullptr;
367}
368
370{
371 if ( index < 0 || index >= mStack.count() )
372 return nullptr;
373
374 return mStack.at( index );
375}
376
378{
379 if ( mStack.count() < 1 )
380 return nullptr;
381
382 return mStack.last();
383}
384
386{
387 if ( !scope )
388 return -1;
389
390 return mStack.indexOf( scope );
391}
392
393int QgsExpressionContext::indexOfScope( const QString &scopeName ) const
394{
395 int index = 0;
396 const auto constMStack = mStack;
397 for ( const QgsExpressionContextScope *scope : constMStack )
398 {
399 if ( scope->name() == scopeName )
400 return index;
401
402 index++;
403 }
404 return -1;
405}
406
408{
409 QStringList names;
410 const auto constMStack = mStack;
411 for ( const QgsExpressionContextScope *scope : constMStack )
412 {
413 names << scope->variableNames();
414 }
415 return qgis::setToList( qgis::listToSet( names ) );
416}
417
419{
420 QStringList allVariables = variableNames();
421 QStringList filtered;
422 const auto constAllVariables = allVariables;
423 for ( const QString &variable : constAllVariables )
424 {
425 if ( variable.startsWith( '_' ) )
426 continue;
427
428 filtered << variable;
429 }
430
431 filtered.sort();
432 return filtered;
433}
434
435bool QgsExpressionContext::isReadOnly( const QString &name ) const
436{
437 const auto constMStack = mStack;
438 for ( const QgsExpressionContextScope *scope : constMStack )
439 {
440 if ( scope->isReadOnly( name ) )
441 return true;
442 }
443 return false;
444}
445
446QString QgsExpressionContext::description( const QString &name ) const
447{
449 return ( scope && !scope->description( name ).isEmpty() ) ? scope->description( name ) : QgsExpression::variableHelpText( name );
450}
451
452bool QgsExpressionContext::hasFunction( const QString &name ) const
453{
454 const auto constMStack = mStack;
455 for ( const QgsExpressionContextScope *scope : constMStack )
456 {
457 if ( scope->hasFunction( name ) )
458 return true;
459 }
460 return false;
461}
462
464{
465 QStringList result;
466 const auto constMStack = mStack;
467 for ( const QgsExpressionContextScope *scope : constMStack )
468 {
469 result << scope->functionNames();
470 }
471 result = qgis::setToList( qgis::listToSet( result ) );
472 result.sort();
473 return result;
474}
475
477{
478 //iterate through stack backwards, so that higher priority variables take precedence
479 QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
480 while ( it != mStack.constBegin() )
481 {
482 --it;
483 if ( ( *it )->hasFunction( name ) )
484 return ( *it )->function( name );
485 }
486 return nullptr;
487}
488
490{
491 return mStack.count();
492}
493
495{
496 mStack.append( scope );
497}
498
499void QgsExpressionContext::appendScopes( const QList<QgsExpressionContextScope *> &scopes )
500{
501 mStack.append( scopes );
502}
503
505{
506 if ( !mStack.isEmpty() )
507 return mStack.takeLast();
508
509 return nullptr;
510}
511
512QList<QgsExpressionContextScope *> QgsExpressionContext::takeScopes()
513{
514 QList<QgsExpressionContextScope *> stack = mStack;
515 mStack.clear();
516 return stack;
517}
518
520{
521 mStack.append( scope );
522 return *this;
523}
524
526{
527 if ( mStack.isEmpty() )
528 mStack.append( new QgsExpressionContextScope() );
529
530 mStack.last()->setFeature( feature );
531}
532
534{
535 for ( const QgsExpressionContextScope *scope : mStack )
536 {
537 if ( scope->hasFeature() )
538 return true;
539 }
540 return false;
541}
542
544{
545 //iterate through stack backwards, so that higher priority variables take precedence
546 QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
547 while ( it != mStack.constBegin() )
548 {
549 --it;
550 if ( ( *it )->hasFeature() )
551 return ( *it )->feature();
552 }
553 return QgsFeature();
554}
555
557{
558 if ( mStack.isEmpty() )
559 mStack.append( new QgsExpressionContextScope() );
560
561 mStack.last()->setGeometry( geometry );
562}
563
565{
566 for ( const QgsExpressionContextScope *scope : mStack )
567 {
568 if ( scope->hasGeometry() )
569 return true;
570 }
571 return false;
572}
573
575{
576 //iterate through stack backwards, so that higher priority variables take precedence
577 QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
578 while ( it != mStack.constBegin() )
579 {
580 --it;
581 if ( ( *it )->hasGeometry() )
582 return ( *it )->geometry();
583 }
584 return QgsGeometry();
585}
586
588{
589 if ( mStack.isEmpty() )
590 mStack.append( new QgsExpressionContextScope() );
591
592 mStack.last()->setFields( fields );
593}
594
596{
597 return qvariant_cast<QgsFields>( variable( QgsExpressionContext::EXPR_FIELDS ) );
598}
599
601{
602 if ( mStack.isEmpty() )
603 mStack.append( new QgsExpressionContextScope() );
604
606 value, true ) );
607}
608
609void QgsExpressionContext::setCachedValue( const QString &key, const QVariant &value ) const
610{
611 mCachedValues.insert( key, value );
612}
613
614bool QgsExpressionContext::hasCachedValue( const QString &key ) const
615{
616 return mCachedValues.contains( key );
617}
618
619QVariant QgsExpressionContext::cachedValue( const QString &key ) const
620{
621 return mCachedValues.value( key, QVariant() );
622}
623
625{
626 mCachedValues.clear();
627}
628
630{
631 mFeedback = feedback;
632}
633
635{
636 return mFeedback;
637}
Single scope for storing variables and functions for use within a QgsExpressionContext.
bool hasVariable(const QString &name) const
Tests whether a variable with the specified name exists in the scope.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the scope.
QString description(const QString &name) const
Returns the translated description for the variable with the specified name (if set).
void readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads scope variables from an XML element.
bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const
Writes scope variables to an XML element.
QgsExpressionFunction * function(const QString &name) const
Retrieves a function from the scope.
QVariant variable(const QString &name) const
Retrieves a variable's value from the scope.
bool removeVariable(const QString &name)
Removes a variable from the context scope, if found.
bool isReadOnly(const QString &name) const
Tests whether the specified variable is read only and should not be editable by users.
void addFunction(const QString &name, QgsScopedExpressionFunction *function)
Adds a function to the scope.
bool hasFeature() const
Returns true if the scope has a feature associated with it.
bool hasFunction(const QString &name) const
Tests whether a function with the specified name exists in the scope.
QString name() const
Returns the friendly display name of the context scope.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
bool isStatic(const QString &name) const
Tests whether the variable with the specified name is static and can be cached.
bool hasGeometry() const
Returns true if the scope has a geometry associated with it.
QStringList filteredVariableNames() const
Returns a filtered and sorted list of variable names contained within the scope.
QStringList functionNames() const
Retrieves a list of names of functions contained in the scope.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
QgsExpressionContextScope & operator=(const QgsExpressionContextScope &other)
QgsExpressionContextScope(const QString &name=QString())
Constructor for QgsExpressionContextScope.
QStringList variableNames() const
Returns a list of variable names contained within the scope.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static const QString EXPR_GEOMETRY_PART_COUNT
Inbuilt variable name for geometry part count variable.
bool hasFunction(const QString &name) const
Checks whether a specified function is contained in the context.
QString description(const QString &name) const
Returns a translated description string for the variable with specified name.
static const QString EXPR_GEOMETRY_POINT_COUNT
Inbuilt variable name for point count variable.
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
QStringList highlightedVariables() const
Returns the current list of variables highlighted within the context.
static const QString EXPR_CLUSTER_SIZE
Inbuilt variable name for cluster size variable.
QStringList functionNames() const
Retrieves a list of function names contained in the context.
static const QString EXPR_GEOMETRY_POINT_NUM
Inbuilt variable name for point number variable.
int indexOfScope(QgsExpressionContextScope *scope) const
Returns the index of the specified scope if it exists within the context.
void setCachedValue(const QString &key, const QVariant &value) const
Sets a value to cache within the expression context.
void clearCachedValues() const
Clears all cached values from the context.
void setHighlightedFunctions(const QStringList &names)
Sets the list of function names intended to be highlighted to the user.
QgsGeometry geometry() const
Convenience function for retrieving the geometry for the context, if set.
bool isHighlightedFunction(const QString &name) const
Returns true if the specified function name is intended to be highlighted to the user.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
QgsExpressionContext & operator=(const QgsExpressionContext &other)
QgsExpressionContext()=default
Constructor for QgsExpressionContext.
static const QString EXPR_FIELDS
Inbuilt variable name for fields storage.
QStringList filteredVariableNames() const
Returns a filtered list of variables names set by all scopes in the context.
void setGeometry(const QgsGeometry &geometry)
Convenience function for setting a geometry for the context.
static const QString EXPR_GEOMETRY_RING_NUM
Inbuilt variable name for geometry ring number variable.
bool isHighlightedVariable(const QString &name) const
Returns true if the specified variable name is intended to be highlighted to the user.
QgsExpressionContextScope * activeScopeForVariable(const QString &name)
Returns the currently active scope from the context for a specified variable name.
static const QString EXPR_GEOMETRY_PART_NUM
Inbuilt variable name for geometry part number variable.
static const QString EXPR_SYMBOL_COLOR
Inbuilt variable name for symbol color variable.
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
QList< QgsExpressionContextScope * > scopes()
Returns a list of scopes contained within the stack.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
bool hasVariable(const QString &name) const
Check whether a variable is specified by any scope within the context.
QList< QgsExpressionContextScope * > takeScopes()
Returns all scopes from this context and remove them, leaving this context without any context.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the expression to check if evaluation sh...
void setFeedback(QgsFeedback *feedback)
Attach a feedback object that can be queried regularly by the expression engine to check if expressio...
int scopeCount() const
Returns the number of scopes contained in the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void setHighlightedVariables(const QStringList &variableNames)
Sets the list of variable names within the context intended to be highlighted to the user.
static const QString EXPR_SYMBOL_ANGLE
Inbuilt variable name for symbol angle variable.
bool isReadOnly(const QString &name) const
Returns whether a variable is read only, and should not be modifiable by users.
bool hasGeometry() const
Returns true if the context has a geometry associated with it.
QgsExpressionFunction * function(const QString &name) const
Fetches a matching function from the context.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
QVariantMap variablesToMap() const
Returns a map of variable name to value representing all the expression variables contained by the co...
bool hasCachedValue(const QString &key) const
Returns true if the expression context contains a cached value with a matching key.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
QgsExpressionContext & operator<<(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
static const QString EXPR_ORIGINAL_VALUE
Inbuilt variable name for value original value variable.
static const QString EXPR_CLUSTER_COLOR
Inbuilt variable name for cluster color variable.
QStringList variableNames() const
Returns a list of variables names set by all scopes in the context.
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
QgsExpressionContextScope * scope(int index)
Returns the scope at the specified index within the context.
QVariant cachedValue(const QString &key) const
Returns the matching cached value, if set.
bool hasFeature() const
Returns true if the context has a feature associated with it.
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
A abstract base class for defining QgsExpression functions.
static QString variableHelpText(const QString &variableName)
Returns the help text for a specified variable.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
Container of fields for a vector layer.
Definition: qgsfields.h:45
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
The class is used as a container of context for various read/write operations on other objects.
Expression function for use within a QgsExpressionContextScope.
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
Single variable definition for use within a QgsExpressionContextScope.