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