QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 
16 #include "qgsexpressioncontext.h"
17 #include "qgslogger.h"
18 #include "qgsxmlutils.h"
19 #include "qgsexpression.h"
20 
21 const QString QgsExpressionContext::EXPR_FIELDS( QStringLiteral( "_fields_" ) );
22 const QString QgsExpressionContext::EXPR_ORIGINAL_VALUE( QStringLiteral( "value" ) );
23 const QString QgsExpressionContext::EXPR_SYMBOL_COLOR( QStringLiteral( "symbol_color" ) );
24 const QString QgsExpressionContext::EXPR_SYMBOL_ANGLE( QStringLiteral( "symbol_angle" ) );
25 const QString QgsExpressionContext::EXPR_GEOMETRY_PART_COUNT( QStringLiteral( "geometry_part_count" ) );
26 const QString QgsExpressionContext::EXPR_GEOMETRY_PART_NUM( QStringLiteral( "geometry_part_num" ) );
27 const QString QgsExpressionContext::EXPR_GEOMETRY_RING_NUM( QStringLiteral( "geometry_ring_num" ) );
28 const QString QgsExpressionContext::EXPR_GEOMETRY_POINT_COUNT( QStringLiteral( "geometry_point_count" ) );
29 const QString QgsExpressionContext::EXPR_GEOMETRY_POINT_NUM( QStringLiteral( "geometry_point_num" ) );
30 const QString QgsExpressionContext::EXPR_CLUSTER_SIZE( QStringLiteral( "cluster_size" ) );
31 const 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 {
49  QHash<QString, QgsScopedExpressionFunction * >::const_iterator it = other.mFunctions.constBegin();
50  for ( ; it != other.mFunctions.constEnd(); ++it )
51  {
52  mFunctions.insert( it.key(), it.value()->clone() );
53  }
54 }
55 
57 {
58  mName = other.mName;
59  mVariables = other.mVariables;
60  mHasFeature = other.mHasFeature;
61  mFeature = other.mFeature;
62 
63  qDeleteAll( mFunctions );
64  mFunctions.clear();
65  QHash<QString, QgsScopedExpressionFunction * >::const_iterator it = other.mFunctions.constBegin();
66  for ( ; it != other.mFunctions.constEnd(); ++it )
67  {
68  mFunctions.insert( it.key(), it.value()->clone() );
69  }
70 
71  return *this;
72 }
73 
75 {
76  qDeleteAll( mFunctions );
77 }
78 
79 void QgsExpressionContextScope::setVariable( const QString &name, const QVariant &value, bool isStatic )
80 {
81  if ( mVariables.contains( name ) )
82  {
83  StaticVariable existing = mVariables.value( name );
84  existing.value = value;
85  existing.isStatic = isStatic;
86  addVariable( existing );
87  }
88  else
89  {
91  }
92 }
93 
95 {
96  mVariables.insert( variable.name, variable );
97 }
98 
99 bool QgsExpressionContextScope::removeVariable( const QString &name )
100 {
101  return mVariables.remove( name ) > 0;
102 }
103 
104 bool QgsExpressionContextScope::hasVariable( const QString &name ) const
105 {
106  return mVariables.contains( name );
107 }
108 
109 QVariant QgsExpressionContextScope::variable( const QString &name ) const
110 {
111  return hasVariable( name ) ? mVariables.value( name ).value : QVariant();
112 }
113 
115 {
116  QStringList names = mVariables.keys();
117  return names;
118 }
119 
121 class QgsExpressionContextVariableCompare
122 {
123  public:
124  explicit QgsExpressionContextVariableCompare( const QgsExpressionContextScope &scope )
125  : mScope( scope )
126  { }
127 
128  bool operator()( const QString &a, const QString &b ) const
129  {
130  bool aReadOnly = mScope.isReadOnly( a );
131  bool bReadOnly = mScope.isReadOnly( b );
132  if ( aReadOnly != bReadOnly )
133  return aReadOnly;
134  return QString::localeAwareCompare( a, b ) < 0;
135  }
136 
137  private:
138  const QgsExpressionContextScope &mScope;
139 };
141 
143 {
144  QStringList allVariables = mVariables.keys();
145  QStringList filtered;
146  const auto constAllVariables = allVariables;
147  for ( const QString &variable : constAllVariables )
148  {
149  if ( variable.startsWith( '_' ) )
150  continue;
151 
152  filtered << variable;
153  }
154  QgsExpressionContextVariableCompare cmp( *this );
155  std::sort( filtered.begin(), filtered.end(), cmp );
156 
157  return filtered;
158 }
159 
160 bool QgsExpressionContextScope::isReadOnly( const QString &name ) const
161 {
162  return hasVariable( name ) ? mVariables.value( name ).readOnly : false;
163 }
164 
165 bool QgsExpressionContextScope::isStatic( const QString &name ) const
166 {
167  return hasVariable( name ) ? mVariables.value( name ).isStatic : false;
168 }
169 
170 QString QgsExpressionContextScope::description( const QString &name ) const
171 {
172  return hasVariable( name ) ? mVariables.value( name ).description : QString();
173 }
174 
175 bool QgsExpressionContextScope::hasFunction( const QString &name ) const
176 {
177  return mFunctions.contains( name );
178 }
179 
181 {
182  return mFunctions.contains( name ) ? mFunctions.value( name ) : nullptr;
183 }
184 
186 {
187  return mFunctions.keys();
188 }
189 
191 {
192  mFunctions.insert( name, function );
193 }
194 
195 
197 {
198  addVariable( StaticVariable( QgsExpressionContext::EXPR_FIELDS, QVariant::fromValue( fields ), true ) );
199 }
200 
201 void QgsExpressionContextScope::readXml( const QDomElement &element, const QgsReadWriteContext & )
202 {
203  const QDomNodeList variablesNodeList = element.childNodes();
204  for ( int i = 0; i < variablesNodeList.size(); ++i )
205  {
206  const QDomElement variableElement = variablesNodeList.at( i ).toElement();
207  const QString key = variableElement.attribute( QStringLiteral( "name" ) );
208  const QVariant value = QgsXmlUtils::readVariant( variableElement.firstChildElement( QStringLiteral( "Option" ) ) );
209  setVariable( key, value );
210  }
211 }
212 
213 bool QgsExpressionContextScope::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext & ) const
214 {
215  for ( auto it = mVariables.constBegin(); it != mVariables.constEnd(); ++it )
216  {
217  QDomElement varElem = document.createElement( QStringLiteral( "Variable" ) );
218  varElem.setAttribute( QStringLiteral( "name" ), it.key() );
219  QDomElement valueElem = QgsXmlUtils::writeVariant( it.value().value, document );
220  varElem.appendChild( valueElem );
221  element.appendChild( varElem );
222  }
223  return true;
224 }
225 
226 
227 //
228 // QgsExpressionContext
229 //
230 
231 QgsExpressionContext::QgsExpressionContext( const QList<QgsExpressionContextScope *> &scopes )
232  : mStack( scopes )
233 {
234 }
235 
237 {
238  for ( const QgsExpressionContextScope *scope : std::as_const( other.mStack ) )
239  {
240  mStack << new QgsExpressionContextScope( *scope );
241  }
242  mHighlightedVariables = other.mHighlightedVariables;
243  mHighlightedFunctions = other.mHighlightedFunctions;
244  mCachedValues = other.mCachedValues;
245 }
246 
248 {
249  if ( this != &other )
250  {
251  qDeleteAll( mStack );
252  // move the stack over
253  mStack = other.mStack;
254  other.mStack.clear();
255 
256  mHighlightedVariables = other.mHighlightedVariables;
257  mHighlightedFunctions = other.mHighlightedFunctions;
258  mCachedValues = other.mCachedValues;
259  }
260  return *this;
261 }
262 
264 {
265  if ( &other == this )
266  return *this;
267 
268  qDeleteAll( mStack );
269  mStack.clear();
270  for ( const QgsExpressionContextScope *scope : std::as_const( other.mStack ) )
271  {
272  mStack << new QgsExpressionContextScope( *scope );
273  }
274  mHighlightedVariables = other.mHighlightedVariables;
275  mHighlightedFunctions = other.mHighlightedFunctions;
276  mCachedValues = other.mCachedValues;
277  return *this;
278 }
279 
281 {
282  qDeleteAll( mStack );
283  mStack.clear();
284 }
285 
286 bool QgsExpressionContext::hasVariable( const QString &name ) const
287 {
288  const auto constMStack = mStack;
289  for ( const QgsExpressionContextScope *scope : constMStack )
290  {
291  if ( scope->hasVariable( name ) )
292  return true;
293  }
294  return false;
295 }
296 
297 QVariant QgsExpressionContext::variable( const QString &name ) const
298 {
300  return scope ? scope->variable( name ) : QVariant();
301 }
302 
304 {
305  QStringList names = variableNames();
306  QVariantMap m;
307  const auto constNames = names;
308  for ( const QString &name : constNames )
309  {
310  m.insert( name, variable( name ) );
311  }
312  return m;
313 }
314 
315 bool QgsExpressionContext::isHighlightedVariable( const QString &name ) const
316 {
317  return mHighlightedVariables.contains( name );
318 }
319 
321 {
322  return mHighlightedVariables;
323 }
324 
325 void QgsExpressionContext::setHighlightedVariables( const QStringList &variableNames )
326 {
327  mHighlightedVariables = variableNames;
328 }
329 
330 bool QgsExpressionContext::isHighlightedFunction( const QString &name ) const
331 {
332  return mHighlightedFunctions.contains( name );
333 }
334 
335 void QgsExpressionContext::setHighlightedFunctions( const QStringList &names )
336 {
337  mHighlightedFunctions = names;
338 }
339 
341 {
342  //iterate through stack backwards, so that higher priority variables take precedence
343  QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
344  while ( it != mStack.constBegin() )
345  {
346  --it;
347  if ( ( *it )->hasVariable( name ) )
348  return ( *it );
349  }
350  return nullptr;
351 }
352 
354 {
355  //iterate through stack backwards, so that higher priority variables take precedence
356  QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
357  while ( it != mStack.constBegin() )
358  {
359  --it;
360  if ( ( *it )->hasVariable( name ) )
361  return ( *it );
362  }
363  return nullptr;
364 }
365 
367 {
368  if ( index < 0 || index >= mStack.count() )
369  return nullptr;
370 
371  return mStack.at( index );
372 }
373 
375 {
376  if ( mStack.count() < 1 )
377  return nullptr;
378 
379  return mStack.last();
380 }
381 
383 {
384  if ( !scope )
385  return -1;
386 
387  return mStack.indexOf( scope );
388 }
389 
390 int QgsExpressionContext::indexOfScope( const QString &scopeName ) const
391 {
392  int index = 0;
393  const auto constMStack = mStack;
394  for ( const QgsExpressionContextScope *scope : constMStack )
395  {
396  if ( scope->name() == scopeName )
397  return index;
398 
399  index++;
400  }
401  return -1;
402 }
403 
405 {
406  QStringList names;
407  const auto constMStack = mStack;
408  for ( const QgsExpressionContextScope *scope : constMStack )
409  {
410  names << scope->variableNames();
411  }
412  return qgis::setToList( qgis::listToSet( names ) );
413 }
414 
416 {
417  QStringList allVariables = variableNames();
418  QStringList filtered;
419  const auto constAllVariables = allVariables;
420  for ( const QString &variable : constAllVariables )
421  {
422  if ( variable.startsWith( '_' ) )
423  continue;
424 
425  filtered << variable;
426  }
427 
428  filtered.sort();
429  return filtered;
430 }
431 
432 bool QgsExpressionContext::isReadOnly( const QString &name ) const
433 {
434  const auto constMStack = mStack;
435  for ( const QgsExpressionContextScope *scope : constMStack )
436  {
437  if ( scope->isReadOnly( name ) )
438  return true;
439  }
440  return false;
441 }
442 
443 QString QgsExpressionContext::description( const QString &name ) const
444 {
446  return ( scope && !scope->description( name ).isEmpty() ) ? scope->description( name ) : QgsExpression::variableHelpText( name );
447 }
448 
449 bool QgsExpressionContext::hasFunction( const QString &name ) const
450 {
451  const auto constMStack = mStack;
452  for ( const QgsExpressionContextScope *scope : constMStack )
453  {
454  if ( scope->hasFunction( name ) )
455  return true;
456  }
457  return false;
458 }
459 
461 {
462  QStringList result;
463  const auto constMStack = mStack;
464  for ( const QgsExpressionContextScope *scope : constMStack )
465  {
466  result << scope->functionNames();
467  }
468  result = qgis::setToList( qgis::listToSet( result ) );
469  result.sort();
470  return result;
471 }
472 
474 {
475  //iterate through stack backwards, so that higher priority variables take precedence
476  QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
477  while ( it != mStack.constBegin() )
478  {
479  --it;
480  if ( ( *it )->hasFunction( name ) )
481  return ( *it )->function( name );
482  }
483  return nullptr;
484 }
485 
487 {
488  return mStack.count();
489 }
490 
492 {
493  mStack.append( scope );
494 }
495 
496 void QgsExpressionContext::appendScopes( const QList<QgsExpressionContextScope *> &scopes )
497 {
498  mStack.append( scopes );
499 }
500 
502 {
503  if ( !mStack.isEmpty() )
504  return mStack.takeLast();
505 
506  return nullptr;
507 }
508 
509 QList<QgsExpressionContextScope *> QgsExpressionContext::takeScopes()
510 {
511  QList<QgsExpressionContextScope *> stack = mStack;
512  mStack.clear();
513  return stack;
514 }
515 
517 {
518  mStack.append( scope );
519  return *this;
520 }
521 
523 {
524  if ( mStack.isEmpty() )
525  mStack.append( new QgsExpressionContextScope() );
526 
527  mStack.last()->setFeature( feature );
528 }
529 
531 {
532  const auto constMStack = mStack;
533  for ( const QgsExpressionContextScope *scope : constMStack )
534  {
535  if ( scope->hasFeature() )
536  return true;
537  }
538  return false;
539 }
540 
542 {
543  //iterate through stack backwards, so that higher priority variables take precedence
544  QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
545  while ( it != mStack.constBegin() )
546  {
547  --it;
548  if ( ( *it )->hasFeature() )
549  return ( *it )->feature();
550  }
551  return QgsFeature();
552 }
553 
555 {
556  if ( mStack.isEmpty() )
557  mStack.append( new QgsExpressionContextScope() );
558 
559  mStack.last()->setFields( fields );
560 }
561 
563 {
564  return qvariant_cast<QgsFields>( variable( QgsExpressionContext::EXPR_FIELDS ) );
565 }
566 
568 {
569  if ( mStack.isEmpty() )
570  mStack.append( new QgsExpressionContextScope() );
571 
573  value, true ) );
574 }
575 
576 void QgsExpressionContext::setCachedValue( const QString &key, const QVariant &value ) const
577 {
578  mCachedValues.insert( key, value );
579 }
580 
581 bool QgsExpressionContext::hasCachedValue( const QString &key ) const
582 {
583  return mCachedValues.contains( key );
584 }
585 
586 QVariant QgsExpressionContext::cachedValue( const QString &key ) const
587 {
588  return mCachedValues.value( key, QVariant() );
589 }
590 
592 {
593  mCachedValues.clear();
594 }
595 
597 {
598  mFeedback = feedback;
599 }
600 
602 {
603  return mFeedback;
604 }
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.
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.
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.
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.
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
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.
bool isStatic
A static variable can be cached for the lifetime of a context.