QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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  {
89  addVariable( QgsExpressionContextScope::StaticVariable( name, value, false, isStatic ) );
90  }
91 }
92 
94 {
95  mVariables.insert( variable.name, variable );
96 }
97 
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 
119 bool QgsExpressionContextScope::variableNameSort( const QString &a, const QString &b )
120 {
121  return QString::localeAwareCompare( a, b ) < 0;
122 }
123 
125 class QgsExpressionContextVariableCompare
126 {
127  public:
128  explicit QgsExpressionContextVariableCompare( const QgsExpressionContextScope &scope )
129  : mScope( scope )
130  { }
131 
132  bool operator()( const QString &a, const QString &b ) const
133  {
134  bool aReadOnly = mScope.isReadOnly( a );
135  bool bReadOnly = mScope.isReadOnly( b );
136  if ( aReadOnly != bReadOnly )
137  return aReadOnly;
138  return QString::localeAwareCompare( a, b ) < 0;
139  }
140 
141  private:
142  const QgsExpressionContextScope &mScope;
143 };
145 
147 {
148  QStringList allVariables = mVariables.keys();
149  QStringList filtered;
150  const auto constAllVariables = allVariables;
151  for ( const QString &variable : constAllVariables )
152  {
153  if ( variable.startsWith( '_' ) )
154  continue;
155 
156  filtered << variable;
157  }
158  QgsExpressionContextVariableCompare cmp( *this );
159  std::sort( filtered.begin(), filtered.end(), cmp );
160 
161  return filtered;
162 }
163 
164 bool QgsExpressionContextScope::isReadOnly( const QString &name ) const
165 {
166  return hasVariable( name ) ? mVariables.value( name ).readOnly : false;
167 }
168 
169 bool QgsExpressionContextScope::isStatic( const QString &name ) const
170 {
171  return hasVariable( name ) ? mVariables.value( name ).isStatic : false;
172 }
173 
174 QString QgsExpressionContextScope::description( const QString &name ) const
175 {
176  return hasVariable( name ) ? mVariables.value( name ).description : QString();
177 }
178 
179 bool QgsExpressionContextScope::hasFunction( const QString &name ) const
180 {
181  return mFunctions.contains( name );
182 }
183 
185 {
186  return mFunctions.contains( name ) ? mFunctions.value( name ) : nullptr;
187 }
188 
190 {
191  return mFunctions.keys();
192 }
193 
195 {
196  mFunctions.insert( name, function );
197 }
198 
199 
201 {
202  addVariable( StaticVariable( QgsExpressionContext::EXPR_FIELDS, QVariant::fromValue( fields ), true ) );
203 }
204 
205 void QgsExpressionContextScope::readXml( const QDomElement &element, const QgsReadWriteContext & )
206 {
207  const QDomNodeList variablesNodeList = element.childNodes();
208  for ( int i = 0; i < variablesNodeList.size(); ++i )
209  {
210  const QDomElement variableElement = variablesNodeList.at( i ).toElement();
211  const QString key = variableElement.attribute( QStringLiteral( "name" ) );
212  const QVariant value = QgsXmlUtils::readVariant( variableElement.firstChildElement( QStringLiteral( "Option" ) ) );
213  setVariable( key, value );
214  }
215 }
216 
217 bool QgsExpressionContextScope::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext & ) const
218 {
219  for ( auto it = mVariables.constBegin(); it != mVariables.constEnd(); ++it )
220  {
221  QDomElement varElem = document.createElement( QStringLiteral( "Variable" ) );
222  varElem.setAttribute( QStringLiteral( "name" ), it.key() );
223  QDomElement valueElem = QgsXmlUtils::writeVariant( it.value().value, document );
224  varElem.appendChild( valueElem );
225  element.appendChild( varElem );
226  }
227  return true;
228 }
229 
230 
231 //
232 // QgsExpressionContext
233 //
234 
235 QgsExpressionContext::QgsExpressionContext( const QList<QgsExpressionContextScope *> &scopes )
236  : mStack( scopes )
237 {
238 }
239 
241 {
242  for ( const QgsExpressionContextScope *scope : qgis::as_const( other.mStack ) )
243  {
244  mStack << new QgsExpressionContextScope( *scope );
245  }
246  mHighlightedVariables = other.mHighlightedVariables;
247  mHighlightedFunctions = other.mHighlightedFunctions;
248  mCachedValues = other.mCachedValues;
249 }
250 
252 {
253  if ( this != &other )
254  {
255  qDeleteAll( mStack );
256  // move the stack over
257  mStack = other.mStack;
258  other.mStack.clear();
259 
260  mHighlightedVariables = other.mHighlightedVariables;
261  mHighlightedFunctions = other.mHighlightedFunctions;
262  mCachedValues = other.mCachedValues;
263  }
264  return *this;
265 }
266 
268 {
269  qDeleteAll( mStack );
270  mStack.clear();
271  for ( const QgsExpressionContextScope *scope : qgis::as_const( other.mStack ) )
272  {
273  mStack << new QgsExpressionContextScope( *scope );
274  }
275  mHighlightedVariables = other.mHighlightedVariables;
276  mHighlightedFunctions = other.mHighlightedFunctions;
277  mCachedValues = other.mCachedValues;
278  return *this;
279 }
280 
282 {
283  qDeleteAll( mStack );
284  mStack.clear();
285 }
286 
287 bool QgsExpressionContext::hasVariable( const QString &name ) const
288 {
289  const auto constMStack = mStack;
290  for ( const QgsExpressionContextScope *scope : constMStack )
291  {
292  if ( scope->hasVariable( name ) )
293  return true;
294  }
295  return false;
296 }
297 
298 QVariant QgsExpressionContext::variable( const QString &name ) const
299 {
301  return scope ? scope->variable( name ) : QVariant();
302 }
303 
305 {
306  QStringList names = variableNames();
307  QVariantMap m;
308  const auto constNames = names;
309  for ( const QString &name : constNames )
310  {
311  m.insert( name, variable( name ) );
312  }
313  return m;
314 }
315 
316 bool QgsExpressionContext::isHighlightedVariable( const QString &name ) const
317 {
318  return mHighlightedVariables.contains( name );
319 }
320 
322 {
323  return mHighlightedVariables;
324 }
325 
327 {
328  mHighlightedVariables = variableNames;
329 }
330 
331 bool QgsExpressionContext::isHighlightedFunction( const QString &name ) const
332 {
333  return mHighlightedFunctions.contains( name );
334 }
335 
336 void QgsExpressionContext::setHighlightedFunctions( const QStringList &names )
337 {
338  mHighlightedFunctions = names;
339 }
340 
342 {
343  //iterate through stack backwards, so that higher priority variables take precedence
344  QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
345  while ( it != mStack.constBegin() )
346  {
347  --it;
348  if ( ( *it )->hasVariable( name ) )
349  return ( *it );
350  }
351  return nullptr;
352 }
353 
355 {
356  //iterate through stack backwards, so that higher priority variables take precedence
357  QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
358  while ( it != mStack.constBegin() )
359  {
360  --it;
361  if ( ( *it )->hasVariable( name ) )
362  return ( *it );
363  }
364  return nullptr;
365 }
366 
368 {
369  if ( index < 0 || index >= mStack.count() )
370  return nullptr;
371 
372  return mStack.at( index );
373 }
374 
376 {
377  if ( mStack.count() < 1 )
378  return nullptr;
379 
380  return mStack.last();
381 }
382 
384 {
385  if ( !scope )
386  return -1;
387 
388  return mStack.indexOf( scope );
389 }
390 
391 int QgsExpressionContext::indexOfScope( const QString &scopeName ) const
392 {
393  int index = 0;
394  const auto constMStack = mStack;
395  for ( const QgsExpressionContextScope *scope : constMStack )
396  {
397  if ( scope->name() == scopeName )
398  return index;
399 
400  index++;
401  }
402  return -1;
403 }
404 
406 {
407  QStringList names;
408  const auto constMStack = mStack;
409  for ( const QgsExpressionContextScope *scope : constMStack )
410  {
411  names << scope->variableNames();
412  }
413  return names.toSet().toList();
414 }
415 
417 {
418  QStringList allVariables = variableNames();
419  QStringList filtered;
420  const auto constAllVariables = allVariables;
421  for ( const QString &variable : constAllVariables )
422  {
423  if ( variable.startsWith( '_' ) )
424  continue;
425 
426  filtered << variable;
427  }
428 
429  filtered.sort();
430  return filtered;
431 }
432 
433 bool QgsExpressionContext::isReadOnly( const QString &name ) const
434 {
435  const auto constMStack = mStack;
436  for ( const QgsExpressionContextScope *scope : constMStack )
437  {
438  if ( scope->isReadOnly( name ) )
439  return true;
440  }
441  return false;
442 }
443 
444 QString QgsExpressionContext::description( const QString &name ) const
445 {
447  return ( scope && !scope->description( name ).isEmpty() ) ? scope->description( name ) : QgsExpression::variableHelpText( name );
448 }
449 
450 bool QgsExpressionContext::hasFunction( const QString &name ) const
451 {
452  const auto constMStack = mStack;
453  for ( const QgsExpressionContextScope *scope : constMStack )
454  {
455  if ( scope->hasFunction( name ) )
456  return true;
457  }
458  return false;
459 }
460 
462 {
463  QStringList result;
464  const auto constMStack = mStack;
465  for ( const QgsExpressionContextScope *scope : constMStack )
466  {
467  result << scope->functionNames();
468  }
469  result = result.toSet().toList();
470  result.sort();
471  return result;
472 }
473 
475 {
476  //iterate through stack backwards, so that higher priority variables take precedence
477  QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
478  while ( it != mStack.constBegin() )
479  {
480  --it;
481  if ( ( *it )->hasFunction( name ) )
482  return ( *it )->function( name );
483  }
484  return nullptr;
485 }
486 
488 {
489  return mStack.count();
490 }
491 
493 {
494  mStack.append( scope );
495 }
496 
497 void QgsExpressionContext::appendScopes( const QList<QgsExpressionContextScope *> &scopes )
498 {
499  mStack.append( scopes );
500 }
501 
503 {
504  if ( !mStack.isEmpty() )
505  return mStack.takeLast();
506 
507  return nullptr;
508 }
509 
510 QList<QgsExpressionContextScope *> QgsExpressionContext::takeScopes()
511 {
512  QList<QgsExpressionContextScope *> stack = mStack;
513  mStack.clear();
514  return stack;
515 }
516 
518 {
519  mStack.append( scope );
520  return *this;
521 }
522 
524 {
525  if ( mStack.isEmpty() )
526  mStack.append( new QgsExpressionContextScope() );
527 
528  mStack.last()->setFeature( feature );
529 }
530 
532 {
533  const auto constMStack = mStack;
534  for ( const QgsExpressionContextScope *scope : constMStack )
535  {
536  if ( scope->hasFeature() )
537  return true;
538  }
539  return false;
540 }
541 
543 {
544  //iterate through stack backwards, so that higher priority variables take precedence
545  QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
546  while ( it != mStack.constBegin() )
547  {
548  --it;
549  if ( ( *it )->hasFeature() )
550  return ( *it )->feature();
551  }
552  return QgsFeature();
553 }
554 
556 {
557  if ( mStack.isEmpty() )
558  mStack.append( new QgsExpressionContextScope() );
559 
560  mStack.last()->setFields( fields );
561 }
562 
564 {
565  return qvariant_cast<QgsFields>( variable( QgsExpressionContext::EXPR_FIELDS ) );
566 }
567 
569 {
570  if ( mStack.isEmpty() )
571  mStack.append( new QgsExpressionContextScope() );
572 
574  value, true ) );
575 }
576 
577 void QgsExpressionContext::setCachedValue( const QString &key, const QVariant &value ) const
578 {
579  mCachedValues.insert( key, value );
580 }
581 
582 bool QgsExpressionContext::hasCachedValue( const QString &key ) const
583 {
584  return mCachedValues.contains( key );
585 }
586 
587 QVariant QgsExpressionContext::cachedValue( const QString &key ) const
588 {
589  return mCachedValues.value( key, QVariant() );
590 }
591 
593 {
594  mCachedValues.clear();
595 }
bool hasVariable(const QString &name) const
Tests whether a variable with the specified name exists in the scope.
static const QString EXPR_ORIGINAL_VALUE
Inbuilt variable name for value original value variable.
QVariant cachedValue(const QString &key) const
Returns the matching cached value, if set.
The class is used as a container of context for various read/write operations on other objects...
QString description(const QString &name) const
Returns the translated description for the variable with the specified name (if set).
static const QString EXPR_CLUSTER_COLOR
Inbuilt variable name for cluster color variable.
Single variable definition for use within a QgsExpressionContextScope.
void setCachedValue(const QString &key, const QVariant &value) const
Sets a value to cache within the expression context.
bool isHighlightedFunction(const QString &name) const
Returns true if the specified function name is intended to be highlighted to the user.
QgsExpressionContextScope * scope(int index)
Returns the scope at the specified index within the context.
bool isReadOnly(const QString &name) const
Returns whether a variable is read only, and should not be modifiable by users.
static const QString EXPR_GEOMETRY_POINT_COUNT
Inbuilt variable name for point count variable.
void readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads scope variables from an XML element.
QStringList filteredVariableNames() const
Returns a filtered list of variables names set by all scopes in the context.
QgsExpressionContext & operator=(const QgsExpressionContext &other)
bool hasFunction(const QString &name) const
Checks whether a specified function is contained in the context.
void addFunction(const QString &name, QgsScopedExpressionFunction *function)
Adds a function to the scope.
QgsExpressionContextScope(const QString &name=QString())
Constructor for QgsExpressionContextScope.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QgsExpressionContextScope * activeScopeForVariable(const QString &name)
Returns the currently active scope from the context for a specified variable name.
QStringList variableNames() const
Returns a list of variable names contained within the scope.
bool hasCachedValue(const QString &key) const
Returns true if the expression context contains a cached value with a matching key.
bool isReadOnly(const QString &name) const
Tests whether the specified variable is read only and should not be editable by users.
Container of fields for a vector layer.
Definition: qgsfields.h:42
bool hasVariable(const QString &name) const
Check whether a variable is specified by any scope within the context.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the scope.
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
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:55
QgsExpressionContextScope & operator=(const QgsExpressionContextScope &other)
QList< QgsExpressionContextScope * > takeScopes()
Returns all scopes from this context and remove them, leaving this context without any context...
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
QgsExpressionContext & operator<<(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QString description(const QString &name) const
Returns a translated description string for the variable with specified name.
bool hasFeature() const
Returns true if the context has a feature associated with it.
void setHighlightedFunctions(const QStringList &names)
Sets the list of function names intended to be highlighted to the user.
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.
bool removeVariable(const QString &name)
Removes a variable from the context scope, if found.
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
bool hasFunction(const QString &name) const
Tests whether a function with the specified name exists in the scope.
static const QString EXPR_SYMBOL_ANGLE
Inbuilt variable name for symbol angle variable.
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
int scopeCount() const
Returns the number of scopes contained in the context.
bool isHighlightedVariable(const QString &name) const
Returns true if the specified variable name is intended to be highlighted to the user.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QVariantMap variablesToMap() const
Returns a map of variable name to value representing all the expression variables contained by the co...
QVariant variable(const QString &name) const
Retrieves a variable&#39;s value from the scope.
static const QString EXPR_SYMBOL_COLOR
Inbuilt variable name for symbol color variable.
QgsExpressionFunction * function(const QString &name) const
Retrieves a function from the scope.
static const QString EXPR_FIELDS
Inbuilt variable name for fields storage.
QStringList variableNames() const
Returns a list of variables names set by all scopes in the context.
Single scope for storing variables and functions for use within a QgsExpressionContext.
QStringList functionNames() const
Retrieves a list of names of functions contained in the scope.
bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const
Writes scope variables to an XML element.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
QStringList highlightedVariables() const
Returns the current list of variables highlighted within the context.
A abstract base class for defining QgsExpression functions.
QStringList functionNames() const
Retrieves a list of function names contained in the context.
QgsExpressionContext()=default
Constructor for QgsExpressionContext.
void clearCachedValues() const
Clears all cached values from the context.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
bool isStatic(const QString &name) const
Tests whether the variable with the specified name is static and can be cached.
static const QString EXPR_CLUSTER_SIZE
Inbuilt variable name for cluster size variable.
bool hasFeature() const
Returns true if the scope has a feature associated with it.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
static const QString EXPR_GEOMETRY_POINT_NUM
Inbuilt variable name for point number variable.
void setHighlightedVariables(const QStringList &variableNames)
Sets the list of variable names within the context intended to be highlighted to the user...
void appendScopes(const QList< QgsExpressionContextScope *> &scopes)
Appends a list of scopes to the end of the context.
QString name() const
Returns the friendly display name of the context scope.
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
static const QString EXPR_GEOMETRY_PART_NUM
Inbuilt variable name for geometry part number variable.
bool isStatic
A static variable can be cached for the lifetime of a context.
int indexOfScope(QgsExpressionContextScope *scope) const
Returns the index of the specified scope if it exists within the context.
static const QString EXPR_GEOMETRY_PART_COUNT
Inbuilt variable name for geometry part count variable.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
QStringList filteredVariableNames() const
Returns a filtered and sorted list of variable names contained within the scope.
QList< QgsExpressionContextScope *> scopes()
Returns a list of scopes contained within the stack.
QgsExpressionFunction * function(const QString &name) const
Fetches a matching function from the context.
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
Expression function for use within a QgsExpressionContextScope.