QGIS API Documentation  3.7.0-Master (1205fbf)
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  Q_FOREACH ( const QString &variable, allVariables )
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 
163 bool QgsExpressionContextScope::isReadOnly( const QString &name ) const
164 {
165  return hasVariable( name ) ? mVariables.value( name ).readOnly : false;
166 }
167 
168 bool QgsExpressionContextScope::isStatic( const QString &name ) const
169 {
170  return hasVariable( name ) ? mVariables.value( name ).isStatic : false;
171 }
172 
173 QString QgsExpressionContextScope::description( const QString &name ) const
174 {
175  return hasVariable( name ) ? mVariables.value( name ).description : QString();
176 }
177 
178 bool 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 
204 void 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 
216 bool 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 
234 QgsExpressionContext::QgsExpressionContext( const QList<QgsExpressionContextScope *> &scopes )
235  : mStack( scopes )
236 {
237 }
238 
240 {
241  Q_FOREACH ( const QgsExpressionContextScope *scope, 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  qDeleteAll( mStack );
269  mStack.clear();
270  Q_FOREACH ( const QgsExpressionContextScope *scope, 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  Q_FOREACH ( const QgsExpressionContextScope *scope, mStack )
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  Q_FOREACH ( const QString &name, names )
307  {
308  m.insert( name, variable( name ) );
309  }
310  return m;
311 }
312 
313 bool QgsExpressionContext::isHighlightedVariable( const QString &name ) const
314 {
315  return mHighlightedVariables.contains( name );
316 }
317 
319 {
320  mHighlightedVariables = variableNames;
321 }
322 
323 bool QgsExpressionContext::isHighlightedFunction( const QString &name ) const
324 {
325  return mHighlightedFunctions.contains( name );
326 }
327 
328 void QgsExpressionContext::setHighlightedFunctions( const QStringList &names )
329 {
330  mHighlightedFunctions = names;
331 }
332 
334 {
335  //iterate through stack backwards, so that higher priority variables take precedence
336  QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
337  while ( it != mStack.constBegin() )
338  {
339  --it;
340  if ( ( *it )->hasVariable( name ) )
341  return ( *it );
342  }
343  return nullptr;
344 }
345 
347 {
348  //iterate through stack backwards, so that higher priority variables take precedence
349  QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
350  while ( it != mStack.constBegin() )
351  {
352  --it;
353  if ( ( *it )->hasVariable( name ) )
354  return ( *it );
355  }
356  return nullptr;
357 }
358 
360 {
361  if ( index < 0 || index >= mStack.count() )
362  return nullptr;
363 
364  return mStack.at( index );
365 }
366 
368 {
369  if ( mStack.count() < 1 )
370  return nullptr;
371 
372  return mStack.last();
373 }
374 
376 {
377  if ( !scope )
378  return -1;
379 
380  return mStack.indexOf( scope );
381 }
382 
383 int QgsExpressionContext::indexOfScope( const QString &scopeName ) const
384 {
385  int index = 0;
386  Q_FOREACH ( const QgsExpressionContextScope *scope, mStack )
387  {
388  if ( scope->name() == scopeName )
389  return index;
390 
391  index++;
392  }
393  return -1;
394 }
395 
397 {
398  QStringList names;
399  Q_FOREACH ( const QgsExpressionContextScope *scope, mStack )
400  {
401  names << scope->variableNames();
402  }
403  return names.toSet().toList();
404 }
405 
407 {
408  QStringList allVariables = variableNames();
409  QStringList filtered;
410  Q_FOREACH ( const QString &variable, allVariables )
411  {
412  if ( variable.startsWith( '_' ) )
413  continue;
414 
415  filtered << variable;
416  }
417 
418  filtered.sort();
419  return filtered;
420 }
421 
422 bool QgsExpressionContext::isReadOnly( const QString &name ) const
423 {
424  Q_FOREACH ( const QgsExpressionContextScope *scope, mStack )
425  {
426  if ( scope->isReadOnly( name ) )
427  return true;
428  }
429  return false;
430 }
431 
432 QString QgsExpressionContext::description( const QString &name ) const
433 {
435  return ( scope && !scope->description( name ).isEmpty() ) ? scope->description( name ) : QgsExpression::variableHelpText( name );
436 }
437 
438 bool QgsExpressionContext::hasFunction( const QString &name ) const
439 {
440  Q_FOREACH ( const QgsExpressionContextScope *scope, mStack )
441  {
442  if ( scope->hasFunction( name ) )
443  return true;
444  }
445  return false;
446 }
447 
449 {
450  QStringList result;
451  Q_FOREACH ( const QgsExpressionContextScope *scope, mStack )
452  {
453  result << scope->functionNames();
454  }
455  result = result.toSet().toList();
456  result.sort();
457  return result;
458 }
459 
461 {
462  //iterate through stack backwards, so that higher priority variables take precedence
463  QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
464  while ( it != mStack.constBegin() )
465  {
466  --it;
467  if ( ( *it )->hasFunction( name ) )
468  return ( *it )->function( name );
469  }
470  return nullptr;
471 }
472 
474 {
475  return mStack.count();
476 }
477 
479 {
480  mStack.append( scope );
481 }
482 
483 void QgsExpressionContext::appendScopes( const QList<QgsExpressionContextScope *> &scopes )
484 {
485  mStack.append( scopes );
486 }
487 
489 {
490  if ( !mStack.isEmpty() )
491  return mStack.takeLast();
492 
493  return nullptr;
494 }
495 
496 QList<QgsExpressionContextScope *> QgsExpressionContext::takeScopes()
497 {
498  QList<QgsExpressionContextScope *> stack = mStack;
499  mStack.clear();
500  return stack;
501 }
502 
504 {
505  mStack.append( scope );
506  return *this;
507 }
508 
510 {
511  if ( mStack.isEmpty() )
512  mStack.append( new QgsExpressionContextScope() );
513 
514  mStack.last()->setFeature( feature );
515 }
516 
518 {
519  Q_FOREACH ( const QgsExpressionContextScope *scope, mStack )
520  {
521  if ( scope->hasFeature() )
522  return true;
523  }
524  return false;
525 }
526 
528 {
529  //iterate through stack backwards, so that higher priority variables take precedence
530  QList< QgsExpressionContextScope * >::const_iterator it = mStack.constEnd();
531  while ( it != mStack.constBegin() )
532  {
533  --it;
534  if ( ( *it )->hasFeature() )
535  return ( *it )->feature();
536  }
537  return QgsFeature();
538 }
539 
541 {
542  if ( mStack.isEmpty() )
543  mStack.append( new QgsExpressionContextScope() );
544 
545  mStack.last()->setFields( fields );
546 }
547 
549 {
550  return qvariant_cast<QgsFields>( variable( QgsExpressionContext::EXPR_FIELDS ) );
551 }
552 
554 {
555  if ( mStack.isEmpty() )
556  mStack.append( new QgsExpressionContextScope() );
557 
559  value, true ) );
560 }
561 
562 void QgsExpressionContext::setCachedValue( const QString &key, const QVariant &value ) const
563 {
564  mCachedValues.insert( key, value );
565 }
566 
567 bool QgsExpressionContext::hasCachedValue( const QString &key ) const
568 {
569  return mCachedValues.contains( key );
570 }
571 
572 QVariant QgsExpressionContext::cachedValue( const QString &key ) const
573 {
574  return mCachedValues.value( key, QVariant() );
575 }
576 
578 {
579  mCachedValues.clear();
580 }
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.
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.