QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgsexpression.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsexpression.cpp
3  -------------------
4  begin : August 2011
5  copyright : (C) 2011 Martin Dobias
6  email : wonder.sk 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 "qgsexpression.h"
17 #include "qgsexpressionfunction.h"
18 #include "qgsexpressionnodeimpl.h"
19 #include "qgsfeaturerequest.h"
20 #include "qgscolorramp.h"
21 #include "qgslogger.h"
22 #include "qgsexpressioncontext.h"
23 #include "qgsgeometry.h"
24 #include "qgsproject.h"
26 #include "qgsexpressionutils.h"
27 #include "qgsexpression_p.h"
28 
29 #include <QRegularExpression>
30 
31 // from parser
32 extern QgsExpressionNode *parseExpression( const QString &str, QString &parserErrorMsg, QList<QgsExpression::ParserError> &parserErrors );
33 
34 Q_GLOBAL_STATIC( HelpTextHash, sFunctionHelpTexts )
35 Q_GLOBAL_STATIC( QgsStringMap, sVariableHelpTexts )
36 Q_GLOBAL_STATIC( QgsStringMap, sGroups )
37 
38 HelpTextHash &functionHelpTexts()
39 {
40  return *sFunctionHelpTexts();
41 }
42 
43 bool QgsExpression::checkExpression( const QString &text, const QgsExpressionContext *context, QString &errorMessage )
44 {
45  QgsExpression exp( text );
46  exp.prepare( context );
47  errorMessage = exp.parserErrorString();
48  return !exp.hasParserError();
49 }
50 
51 void QgsExpression::setExpression( const QString &expression )
52 {
53  detach();
54  d->mRootNode = ::parseExpression( expression, d->mParserErrorString, d->mParserErrors );
55  d->mEvalErrorString = QString();
56  d->mExp = expression;
57  d->mIsPrepared = false;
58 }
59 
61 {
62  if ( !d->mExp.isNull() )
63  return d->mExp;
64  else
65  return dump();
66 }
67 
68 QString QgsExpression::quotedColumnRef( QString name )
69 {
70  return QStringLiteral( "\"%1\"" ).arg( name.replace( '\"', QLatin1String( "\"\"" ) ) );
71 }
72 
73 QString QgsExpression::quotedString( QString text )
74 {
75  text.replace( '\'', QLatin1String( "''" ) );
76  text.replace( '\\', QLatin1String( "\\\\" ) );
77  text.replace( '\n', QLatin1String( "\\n" ) );
78  text.replace( '\t', QLatin1String( "\\t" ) );
79  return QStringLiteral( "'%1'" ).arg( text );
80 }
81 
82 QString QgsExpression::quotedValue( const QVariant &value )
83 {
84  return quotedValue( value, value.type() );
85 }
86 
87 QString QgsExpression::quotedValue( const QVariant &value, QVariant::Type type )
88 {
89  if ( value.isNull() )
90  return QStringLiteral( "NULL" );
91 
92  switch ( type )
93  {
94  case QVariant::Int:
95  case QVariant::LongLong:
96  case QVariant::Double:
97  return value.toString();
98 
99  case QVariant::Bool:
100  return value.toBool() ? QStringLiteral( "TRUE" ) : QStringLiteral( "FALSE" );
101 
102  case QVariant::List:
103  {
104  QStringList quotedValues;
105  const QVariantList values = value.toList();
106  quotedValues.reserve( values.count() );
107  for ( const QVariant &v : values )
108  {
109  quotedValues += quotedValue( v );
110  }
111  return QStringLiteral( "array( %1 )" ).arg( quotedValues.join( QLatin1String( ", " ) ) );
112  }
113 
114  default:
115  case QVariant::String:
116  return quotedString( value.toString() );
117  }
118 
119 }
120 
121 bool QgsExpression::isFunctionName( const QString &name )
122 {
123  return functionIndex( name ) != -1;
124 }
125 
126 int QgsExpression::functionIndex( const QString &name )
127 {
128  int count = functionCount();
129  for ( int i = 0; i < count; i++ )
130  {
131  if ( QString::compare( name, QgsExpression::Functions()[i]->name(), Qt::CaseInsensitive ) == 0 )
132  return i;
133  const QStringList aliases = QgsExpression::Functions()[i]->aliases();
134  for ( const QString &alias : aliases )
135  {
136  if ( QString::compare( name, alias, Qt::CaseInsensitive ) == 0 )
137  return i;
138  }
139  }
140  return -1;
141 }
142 
144 {
145  return Functions().size();
146 }
147 
148 
149 QgsExpression::QgsExpression( const QString &expr )
150  : d( new QgsExpressionPrivate )
151 {
152  d->mRootNode = ::parseExpression( expr, d->mParserErrorString, d->mParserErrors );
153  d->mExp = expr;
154  Q_ASSERT( !d->mParserErrorString.isNull() || d->mRootNode );
155 }
156 
158  : d( other.d )
159 {
160  d->ref.ref();
161 }
162 
164 {
165  if ( this != &other )
166  {
167  if ( !d->ref.deref() )
168  {
169  delete d;
170  }
171 
172  d = other.d;
173  d->ref.ref();
174  }
175  return *this;
176 }
177 
178 QgsExpression::operator QString() const
179 {
180  return d->mExp;
181 }
182 
184  : d( new QgsExpressionPrivate )
185 {
186 }
187 
189 {
190  Q_ASSERT( d );
191  if ( !d->ref.deref() )
192  delete d;
193 }
194 
195 bool QgsExpression::operator==( const QgsExpression &other ) const
196 {
197  return ( d == other.d || d->mExp == other.d->mExp );
198 }
199 
201 {
202  return d->mRootNode;
203 }
204 
206 {
207  return d->mParserErrors.count() > 0;
208 }
209 
211 {
212  return d->mParserErrorString;
213 }
214 
215 QList<QgsExpression::ParserError> QgsExpression::parserErrors() const
216 {
217  return d->mParserErrors;
218 }
219 
220 QSet<QString> QgsExpression::referencedColumns() const
221 {
222  if ( !d->mRootNode )
223  return QSet<QString>();
224 
225  return d->mRootNode->referencedColumns();
226 }
227 
229 {
230  if ( !d->mRootNode )
231  return QSet<QString>();
232 
233  return d->mRootNode->referencedVariables();
234 }
235 
237 {
238  if ( !d->mRootNode )
239  return QSet<QString>();
240 
241  return d->mRootNode->referencedFunctions();
242 }
243 
244 QSet<int> QgsExpression::referencedAttributeIndexes( const QgsFields &fields ) const
245 {
246  if ( !d->mRootNode )
247  return QSet<int>();
248 
249  const QSet<QString> referencedFields = d->mRootNode->referencedColumns();
250  QSet<int> referencedIndexes;
251 
252  for ( const QString &fieldName : referencedFields )
253  {
254  if ( fieldName == QgsFeatureRequest::ALL_ATTRIBUTES )
255  {
256  referencedIndexes = qgis::listToSet( fields.allAttributesList() );
257  break;
258  }
259  const int idx = fields.lookupField( fieldName );
260  if ( idx >= 0 )
261  {
262  referencedIndexes << idx;
263  }
264  }
265 
266  return referencedIndexes;
267 }
268 
270 {
271  if ( !d->mRootNode )
272  return false;
273  return d->mRootNode->needsGeometry();
274 }
275 
276 void QgsExpression::initGeomCalculator( const QgsExpressionContext *context )
277 {
278  // Set the geometry calculator from the context if it has not been set by setGeomCalculator()
279  if ( context && ! d->mCalc )
280  {
281  // actually don't do it right away, cos it's expensive to create and only a very small number of expression
282  // functions actually require it. Let's lazily construct it when needed
283  d->mDaEllipsoid = context->variable( QStringLiteral( "project_ellipsoid" ) ).toString();
284  d->mDaCrs = std::make_unique<QgsCoordinateReferenceSystem>( context->variable( QStringLiteral( "_layer_crs" ) ).value<QgsCoordinateReferenceSystem>() );
285  d->mDaTransformContext = std::make_unique<QgsCoordinateTransformContext>( context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>() );
286  }
287 
288  // Set the distance units from the context if it has not been set by setDistanceUnits()
289  if ( context && distanceUnits() == QgsUnitTypes::DistanceUnknownUnit )
290  {
291  QString distanceUnitsStr = context->variable( QStringLiteral( "project_distance_units" ) ).toString();
292  if ( ! distanceUnitsStr.isEmpty() )
294  }
295 
296  // Set the area units from the context if it has not been set by setAreaUnits()
297  if ( context && areaUnits() == QgsUnitTypes::AreaUnknownUnit )
298  {
299  QString areaUnitsStr = context->variable( QStringLiteral( "project_area_units" ) ).toString();
300  if ( ! areaUnitsStr.isEmpty() )
301  setAreaUnits( QgsUnitTypes::stringToAreaUnit( areaUnitsStr ) );
302  }
303 }
304 
305 void QgsExpression::detach()
306 {
307  Q_ASSERT( d );
308 
309  if ( d->ref > 1 )
310  {
311  ( void )d->ref.deref();
312 
313  d = new QgsExpressionPrivate( *d );
314  }
315 }
316 
318 {
319  detach();
320  if ( calc )
321  d->mCalc = std::shared_ptr<QgsDistanceArea>( new QgsDistanceArea( *calc ) );
322  else
323  d->mCalc.reset();
324 }
325 
327 {
328  detach();
329  d->mEvalErrorString = QString();
330  if ( !d->mRootNode )
331  {
332  //re-parse expression. Creation of QgsExpressionContexts may have added extra
333  //known functions since this expression was created, so we have another try
334  //at re-parsing it now that the context must have been created
335  d->mRootNode = ::parseExpression( d->mExp, d->mParserErrorString, d->mParserErrors );
336  }
337 
338  if ( !d->mRootNode )
339  {
340  d->mEvalErrorString = tr( "No root node! Parsing failed?" );
341  return false;
342  }
343 
344  initGeomCalculator( context );
345  d->mIsPrepared = true;
346  return d->mRootNode->prepare( this, context );
347 }
348 
350 {
351  d->mEvalErrorString = QString();
352  if ( !d->mRootNode )
353  {
354  d->mEvalErrorString = tr( "No root node! Parsing failed?" );
355  return QVariant();
356  }
357 
358  return d->mRootNode->eval( this, static_cast<const QgsExpressionContext *>( nullptr ) );
359 }
360 
362 {
363  d->mEvalErrorString = QString();
364  if ( !d->mRootNode )
365  {
366  d->mEvalErrorString = tr( "No root node! Parsing failed?" );
367  return QVariant();
368  }
369 
370  if ( ! d->mIsPrepared )
371  {
372  prepare( context );
373  }
374  return d->mRootNode->eval( this, context );
375 }
376 
378 {
379  return !d->mEvalErrorString.isNull();
380 }
381 
383 {
384  return d->mEvalErrorString;
385 }
386 
388 {
389  d->mEvalErrorString = str;
390 }
391 
392 QString QgsExpression::dump() const
393 {
394  if ( !d->mRootNode )
395  return QString();
396 
397  return d->mRootNode->dump();
398 }
399 
401 {
402  if ( !d->mCalc && d->mDaCrs && d->mDaCrs->isValid() && d->mDaTransformContext )
403  {
404  // calculator IS required, so initialize it now...
405  d->mCalc = std::shared_ptr<QgsDistanceArea>( new QgsDistanceArea() );
406  d->mCalc->setEllipsoid( d->mDaEllipsoid.isEmpty() ? geoNone() : d->mDaEllipsoid );
407  d->mCalc->setSourceCrs( *d->mDaCrs.get(), *d->mDaTransformContext.get() );
408  }
409 
410  return d->mCalc.get();
411 }
412 
414 {
415  return d->mDistanceUnit;
416 }
417 
419 {
420  d->mDistanceUnit = unit;
421 }
422 
424 {
425  return d->mAreaUnit;
426 }
427 
429 {
430  d->mAreaUnit = unit;
431 }
432 
433 QString QgsExpression::replaceExpressionText( const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea )
434 {
435  QString expr_action;
436 
437  int index = 0;
438  while ( index < action.size() )
439  {
440  static const QRegularExpression sRegEx{ QStringLiteral( "\\[%(.*?)%\\]" ), QRegularExpression::MultilineOption | QRegularExpression::DotMatchesEverythingOption };
441 
442  const QRegularExpressionMatch match = sRegEx.match( action, index );
443  if ( !match.hasMatch() )
444  break;
445 
446  const int pos = action.indexOf( sRegEx, index );
447  const int start = index;
448  index = pos + match.capturedLength( 0 );
449  const QString toReplace = match.captured( 1 ).trimmed();
450  QgsDebugMsgLevel( "Found expression: " + toReplace, 3 );
451 
452  QgsExpression exp( toReplace );
453  if ( exp.hasParserError() )
454  {
455  QgsDebugMsg( "Expression parser error: " + exp.parserErrorString() );
456 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 2)
457  expr_action += action.midRef( start, index - start );
458 #else
459  expr_action += QStringView {action}.mid( start, index - start );
460 #endif
461  continue;
462  }
463 
464  if ( distanceArea )
465  {
466  //if QgsDistanceArea specified for area/distance conversion, use it
467  exp.setGeomCalculator( distanceArea );
468  }
469 
470  QVariant result = exp.evaluate( context );
471 
472  if ( exp.hasEvalError() )
473  {
474  QgsDebugMsg( "Expression parser eval error: " + exp.evalErrorString() );
475 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 2)
476  expr_action += action.midRef( start, index - start );
477 #else
478  expr_action += QStringView {action}.mid( start, index - start );
479 #endif
480  continue;
481  }
482 
483  QgsDebugMsgLevel( "Expression result is: " + result.toString(), 3 );
484  expr_action += action.mid( start, pos - start ) + result.toString();
485  }
486 
487 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 2)
488  expr_action += action.midRef( index );
489 #else
490  expr_action += QStringView {action}.mid( index ).toString();
491 #endif
492 
493  return expr_action;
494 }
495 
496 QSet<QString> QgsExpression::referencedVariables( const QString &text )
497 {
498  QSet<QString> variables;
499  int index = 0;
500  while ( index < text.size() )
501  {
502  const thread_local QRegularExpression rx( "\\[%([^\\]]+)%\\]" );
503  const QRegularExpressionMatch match = rx.match( text );
504  if ( !match.hasMatch() )
505  break;
506 
507  index = match.capturedStart() + match.capturedLength();
508  QString to_replace = match.captured( 1 ).trimmed();
509 
510  QgsExpression exp( to_replace );
511  variables.unite( exp.referencedVariables() );
512  }
513 
514  return variables;
515 }
516 
517 double QgsExpression::evaluateToDouble( const QString &text, const double fallbackValue )
518 {
519  bool ok;
520  //first test if text is directly convertible to double
521  // use system locale: e.g. in German locale, user is presented with numbers "1,23" instead of "1.23" in C locale
522  // so we also want to allow user to rewrite it to "5,23" and it is still accepted
523  double convertedValue = QLocale().toDouble( text, &ok );
524  if ( ok )
525  {
526  return convertedValue;
527  }
528 
529  //otherwise try to evaluate as expression
530  QgsExpression expr( text );
531 
532  QgsExpressionContext context;
535 
536  QVariant result = expr.evaluate( &context );
537  convertedValue = result.toDouble( &ok );
538  if ( expr.hasEvalError() || !ok )
539  {
540  return fallbackValue;
541  }
542  return convertedValue;
543 }
544 
545 QString QgsExpression::helpText( QString name )
546 {
547  QgsExpression::initFunctionHelp();
548 
549  if ( !sFunctionHelpTexts()->contains( name ) )
550  return tr( "function help for %1 missing" ).arg( name );
551 
552  const Help &f = ( *sFunctionHelpTexts() )[ name ];
553 
554  name = f.mName;
555  if ( f.mType == tr( "group" ) )
556  {
557  name = group( name );
558  name = name.toLower();
559  }
560 
561  name = name.toHtmlEscaped();
562 
563  QString helpContents( QStringLiteral( "<h3>%1</h3>\n<div class=\"description\"><p>%2</p></div>" )
564  .arg( tr( "%1 %2" ).arg( f.mType, name ),
565  f.mDescription ) );
566 
567  for ( const HelpVariant &v : std::as_const( f.mVariants ) )
568  {
569  if ( f.mVariants.size() > 1 )
570  {
571  helpContents += QStringLiteral( "<h3>%1</h3>\n<div class=\"description\">%2</p></div>" ).arg( v.mName, v.mDescription );
572  }
573 
574  if ( f.mType != tr( "group" ) && f.mType != tr( "expression" ) )
575  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"syntax\">\n" ).arg( tr( "Syntax" ) );
576 
577  if ( f.mType == tr( "operator" ) )
578  {
579  if ( v.mArguments.size() == 1 )
580  {
581  helpContents += QStringLiteral( "<code><span class=\"functionname\">%1</span> <span class=\"argument\">%2</span></code>" )
582  .arg( name, v.mArguments[0].mArg );
583  }
584  else if ( v.mArguments.size() == 2 )
585  {
586  helpContents += QStringLiteral( "<code><span class=\"argument\">%1</span> <span class=\"functionname\">%2</span> <span class=\"argument\">%3</span></code>" )
587  .arg( v.mArguments[0].mArg, name, v.mArguments[1].mArg );
588  }
589  }
590  else if ( f.mType != tr( "group" ) && f.mType != tr( "expression" ) )
591  {
592  helpContents += QStringLiteral( "<code><span class=\"functionname\">%1</span>" ).arg( name );
593 
594  bool hasOptionalArgs = false;
595 
596  if ( f.mType == tr( "function" ) && ( f.mName[0] != '$' || !v.mArguments.isEmpty() || v.mVariableLenArguments ) )
597  {
598  helpContents += '(';
599 
600  QString delim;
601  for ( const HelpArg &a : std::as_const( v.mArguments ) )
602  {
603  if ( !a.mDescOnly )
604  {
605  if ( a.mOptional )
606  {
607  hasOptionalArgs = true;
608  helpContents += QLatin1Char( '[' );
609  }
610 
611  helpContents += delim;
612  helpContents += QStringLiteral( "<span class=\"argument\">%2%3</span>" ).arg(
613  a.mArg,
614  a.mDefaultVal.isEmpty() ? QString() : '=' + a.mDefaultVal
615  );
616 
617  if ( a.mOptional )
618  helpContents += QLatin1Char( ']' );
619  }
620  delim = QStringLiteral( "," );
621  }
622 
623  if ( v.mVariableLenArguments )
624  {
625  helpContents += QChar( 0x2026 );
626  }
627 
628  helpContents += ')';
629  }
630 
631  helpContents += QLatin1String( "</code>" );
632 
633  if ( hasOptionalArgs )
634  {
635  helpContents += QLatin1String( "<br/><br/>" ) + tr( "[ ] marks optional components" );
636  }
637  }
638 
639  if ( !v.mArguments.isEmpty() )
640  {
641  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"arguments\">\n<table>" ).arg( tr( "Arguments" ) );
642 
643  for ( const HelpArg &a : std::as_const( v.mArguments ) )
644  {
645  if ( a.mSyntaxOnly )
646  continue;
647 
648  helpContents += QStringLiteral( "<tr><td class=\"argument\">%1</td><td>%2</td></tr>" ).arg( a.mArg, a.mDescription );
649  }
650 
651  helpContents += QLatin1String( "</table>\n</div>\n" );
652  }
653 
654  if ( !v.mExamples.isEmpty() )
655  {
656  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"examples\">\n<ul>\n" ).arg( tr( "Examples" ) );
657 
658  for ( const HelpExample &e : std::as_const( v.mExamples ) )
659  {
660  helpContents += "<li><code>" + e.mExpression + "</code> &rarr; <code>" + e.mReturns + "</code>";
661 
662  if ( !e.mNote.isEmpty() )
663  helpContents += QStringLiteral( " (%1)" ).arg( e.mNote );
664 
665  helpContents += QLatin1String( "</li>\n" );
666  }
667 
668  helpContents += QLatin1String( "</ul>\n</div>\n" );
669  }
670 
671  if ( !v.mNotes.isEmpty() )
672  {
673  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"notes\"><p>%2</p></div>\n" ).arg( tr( "Notes" ), v.mNotes );
674  }
675  }
676 
677  return helpContents;
678 }
679 
680 QStringList QgsExpression::tags( const QString &name )
681 {
682  QStringList tags = QStringList();
683 
684  QgsExpression::initFunctionHelp();
685 
686  if ( sFunctionHelpTexts()->contains( name ) )
687  {
688  const Help &f = ( *sFunctionHelpTexts() )[ name ];
689 
690  for ( const HelpVariant &v : std::as_const( f.mVariants ) )
691  {
692  tags << v.mTags;
693  }
694  }
695 
696  return tags;
697 }
698 
699 void QgsExpression::initVariableHelp()
700 {
701  if ( !sVariableHelpTexts()->isEmpty() )
702  return;
703 
704  //global variables
705  sVariableHelpTexts()->insert( QStringLiteral( "qgis_version" ), QCoreApplication::translate( "variable_help", "Current QGIS version string." ) );
706  sVariableHelpTexts()->insert( QStringLiteral( "qgis_version_no" ), QCoreApplication::translate( "variable_help", "Current QGIS version number." ) );
707  sVariableHelpTexts()->insert( QStringLiteral( "qgis_release_name" ), QCoreApplication::translate( "variable_help", "Current QGIS release name." ) );
708  sVariableHelpTexts()->insert( QStringLiteral( "qgis_short_version" ), QCoreApplication::translate( "variable_help", "Short QGIS version string." ) );
709  sVariableHelpTexts()->insert( QStringLiteral( "qgis_os_name" ), QCoreApplication::translate( "variable_help", "Operating system name, e.g., 'windows', 'linux' or 'osx'." ) );
710  sVariableHelpTexts()->insert( QStringLiteral( "qgis_platform" ), QCoreApplication::translate( "variable_help", "QGIS platform, e.g., 'desktop' or 'server'." ) );
711  sVariableHelpTexts()->insert( QStringLiteral( "qgis_locale" ), QCoreApplication::translate( "variable_help", "Two letter identifier for current QGIS locale." ) );
712  sVariableHelpTexts()->insert( QStringLiteral( "user_account_name" ), QCoreApplication::translate( "variable_help", "Current user's operating system account name." ) );
713  sVariableHelpTexts()->insert( QStringLiteral( "user_full_name" ), QCoreApplication::translate( "variable_help", "Current user's operating system user name (if available)." ) );
714 
715  //project variables
716  sVariableHelpTexts()->insert( QStringLiteral( "project_title" ), QCoreApplication::translate( "variable_help", "Title of current project." ) );
717  sVariableHelpTexts()->insert( QStringLiteral( "project_path" ), QCoreApplication::translate( "variable_help", "Full path (including file name) of current project." ) );
718  sVariableHelpTexts()->insert( QStringLiteral( "project_folder" ), QCoreApplication::translate( "variable_help", "Folder for current project." ) );
719  sVariableHelpTexts()->insert( QStringLiteral( "project_filename" ), QCoreApplication::translate( "variable_help", "Filename of current project." ) );
720  sVariableHelpTexts()->insert( QStringLiteral( "project_basename" ), QCoreApplication::translate( "variable_help", "Base name of current project's filename (without path and extension)." ) );
721  sVariableHelpTexts()->insert( QStringLiteral( "project_home" ), QCoreApplication::translate( "variable_help", "Home path of current project." ) );
722  sVariableHelpTexts()->insert( QStringLiteral( "project_crs" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of project (e.g., 'EPSG:4326')." ) );
723  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_definition" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of project (full definition)." ) );
724  sVariableHelpTexts()->insert( QStringLiteral( "project_units" ), QCoreApplication::translate( "variable_help", "Unit of the project's CRS." ) );
725  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_description" ), QCoreApplication::translate( "variable_help", "Name of the coordinate reference system of the project." ) );
726  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_acronym" ), QCoreApplication::translate( "variable_help", "Acronym of the coordinate reference system of the project." ) );
727  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_ellipsoid" ), QCoreApplication::translate( "variable_help", "Acronym of the ellipsoid of the coordinate reference system of the project." ) );
728  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_proj4" ), QCoreApplication::translate( "variable_help", "Proj4 definition of the coordinate reference system of the project." ) );
729  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_wkt" ), QCoreApplication::translate( "variable_help", "WKT definition of the coordinate reference system of the project." ) );
730  sVariableHelpTexts()->insert( QStringLiteral( "project_author" ), QCoreApplication::translate( "variable_help", "Project author, taken from project metadata." ) );
731  sVariableHelpTexts()->insert( QStringLiteral( "project_abstract" ), QCoreApplication::translate( "variable_help", "Project abstract, taken from project metadata." ) );
732  sVariableHelpTexts()->insert( QStringLiteral( "project_creation_date" ), QCoreApplication::translate( "variable_help", "Project creation date, taken from project metadata." ) );
733  sVariableHelpTexts()->insert( QStringLiteral( "project_identifier" ), QCoreApplication::translate( "variable_help", "Project identifier, taken from project metadata." ) );
734  sVariableHelpTexts()->insert( QStringLiteral( "project_last_saved" ), QCoreApplication::translate( "variable_help", "Date/time when project was last saved." ) );
735  sVariableHelpTexts()->insert( QStringLiteral( "project_keywords" ), QCoreApplication::translate( "variable_help", "Project keywords, taken from project metadata." ) );
736  sVariableHelpTexts()->insert( QStringLiteral( "project_area_units" ), QCoreApplication::translate( "variable_help", "Area unit for current project, used when calculating areas of geometries." ) );
737  sVariableHelpTexts()->insert( QStringLiteral( "project_distance_units" ), QCoreApplication::translate( "variable_help", "Distance unit for current project, used when calculating lengths of geometries." ) );
738  sVariableHelpTexts()->insert( QStringLiteral( "project_ellipsoid" ), QCoreApplication::translate( "variable_help", "Name of ellipsoid of current project, used when calculating geodetic areas and lengths of geometries." ) );
739  sVariableHelpTexts()->insert( QStringLiteral( "layer_ids" ), QCoreApplication::translate( "variable_help", "List of all map layer IDs from the current project." ) );
740  sVariableHelpTexts()->insert( QStringLiteral( "layers" ), QCoreApplication::translate( "variable_help", "List of all map layers from the current project." ) );
741 
742  //layer variables
743  sVariableHelpTexts()->insert( QStringLiteral( "layer_name" ), QCoreApplication::translate( "variable_help", "Name of current layer." ) );
744  sVariableHelpTexts()->insert( QStringLiteral( "layer_id" ), QCoreApplication::translate( "variable_help", "ID of current layer." ) );
745  sVariableHelpTexts()->insert( QStringLiteral( "layer_crs" ), QCoreApplication::translate( "variable_help", "CRS Authority ID of current layer." ) );
746  sVariableHelpTexts()->insert( QStringLiteral( "layer" ), QCoreApplication::translate( "variable_help", "The current layer." ) );
747 
748  //composition variables
749  sVariableHelpTexts()->insert( QStringLiteral( "layout_name" ), QCoreApplication::translate( "variable_help", "Name of composition." ) );
750  sVariableHelpTexts()->insert( QStringLiteral( "layout_numpages" ), QCoreApplication::translate( "variable_help", "Number of pages in composition." ) );
751  sVariableHelpTexts()->insert( QStringLiteral( "layout_page" ), QCoreApplication::translate( "variable_help", "Current page number in composition." ) );
752  sVariableHelpTexts()->insert( QStringLiteral( "layout_pageheight" ), QCoreApplication::translate( "variable_help", "Composition page height in mm (or specified custom units)." ) );
753  sVariableHelpTexts()->insert( QStringLiteral( "layout_pagewidth" ), QCoreApplication::translate( "variable_help", "Composition page width in mm (or specified custom units)." ) );
754  sVariableHelpTexts()->insert( QStringLiteral( "layout_pageoffsets" ), QCoreApplication::translate( "variable_help", "Array of Y coordinate of the top of each page." ) );
755  sVariableHelpTexts()->insert( QStringLiteral( "layout_dpi" ), QCoreApplication::translate( "variable_help", "Composition resolution (DPI)." ) );
756 
757  //atlas variables
758  sVariableHelpTexts()->insert( QStringLiteral( "atlas_layerid" ), QCoreApplication::translate( "variable_help", "Current atlas coverage layer ID." ) );
759  sVariableHelpTexts()->insert( QStringLiteral( "atlas_layername" ), QCoreApplication::translate( "variable_help", "Current atlas coverage layer name." ) );
760  sVariableHelpTexts()->insert( QStringLiteral( "atlas_totalfeatures" ), QCoreApplication::translate( "variable_help", "Total number of features in atlas." ) );
761  sVariableHelpTexts()->insert( QStringLiteral( "atlas_featurenumber" ), QCoreApplication::translate( "variable_help", "Current atlas feature number." ) );
762  sVariableHelpTexts()->insert( QStringLiteral( "atlas_filename" ), QCoreApplication::translate( "variable_help", "Current atlas file name." ) );
763  sVariableHelpTexts()->insert( QStringLiteral( "atlas_pagename" ), QCoreApplication::translate( "variable_help", "Current atlas page name." ) );
764  sVariableHelpTexts()->insert( QStringLiteral( "atlas_feature" ), QCoreApplication::translate( "variable_help", "Current atlas feature (as feature object)." ) );
765  sVariableHelpTexts()->insert( QStringLiteral( "atlas_featureid" ), QCoreApplication::translate( "variable_help", "Current atlas feature ID." ) );
766  sVariableHelpTexts()->insert( QStringLiteral( "atlas_geometry" ), QCoreApplication::translate( "variable_help", "Current atlas feature geometry." ) );
767 
768  //layout item variables
769  sVariableHelpTexts()->insert( QStringLiteral( "item_id" ), QCoreApplication::translate( "variable_help", "Layout item user-assigned ID (not necessarily unique)." ) );
770  sVariableHelpTexts()->insert( QStringLiteral( "item_uuid" ), QCoreApplication::translate( "variable_help", "layout item unique ID." ) );
771  sVariableHelpTexts()->insert( QStringLiteral( "item_left" ), QCoreApplication::translate( "variable_help", "Left position of layout item (in mm)." ) );
772  sVariableHelpTexts()->insert( QStringLiteral( "item_top" ), QCoreApplication::translate( "variable_help", "Top position of layout item (in mm)." ) );
773  sVariableHelpTexts()->insert( QStringLiteral( "item_width" ), QCoreApplication::translate( "variable_help", "Width of layout item (in mm)." ) );
774  sVariableHelpTexts()->insert( QStringLiteral( "item_height" ), QCoreApplication::translate( "variable_help", "Height of layout item (in mm)." ) );
775 
776  //map settings item variables
777  sVariableHelpTexts()->insert( QStringLiteral( "map_id" ), QCoreApplication::translate( "variable_help", "ID of current map destination. This will be 'canvas' for canvas renders, and the item ID for layout map renders." ) );
778  sVariableHelpTexts()->insert( QStringLiteral( "map_rotation" ), QCoreApplication::translate( "variable_help", "Current rotation of map." ) );
779  sVariableHelpTexts()->insert( QStringLiteral( "map_scale" ), QCoreApplication::translate( "variable_help", "Current scale of map." ) );
780  sVariableHelpTexts()->insert( QStringLiteral( "map_extent" ), QCoreApplication::translate( "variable_help", "Geometry representing the current extent of the map." ) );
781  sVariableHelpTexts()->insert( QStringLiteral( "map_extent_center" ), QCoreApplication::translate( "variable_help", "Center of map." ) );
782  sVariableHelpTexts()->insert( QStringLiteral( "map_extent_width" ), QCoreApplication::translate( "variable_help", "Width of map." ) );
783  sVariableHelpTexts()->insert( QStringLiteral( "map_extent_height" ), QCoreApplication::translate( "variable_help", "Height of map." ) );
784  sVariableHelpTexts()->insert( QStringLiteral( "map_crs" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of map (e.g., 'EPSG:4326')." ) );
785  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_description" ), QCoreApplication::translate( "variable_help", "Name of the coordinate reference system of the map." ) );
786  sVariableHelpTexts()->insert( QStringLiteral( "map_units" ), QCoreApplication::translate( "variable_help", "Units for map measurements." ) );
787  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_definition" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of the map (full definition)." ) );
788  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_acronym" ), QCoreApplication::translate( "variable_help", "Acronym of the coordinate reference system of the map." ) );
789  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_projection" ), QCoreApplication::translate( "variable_help", "Projection method used by the coordinate reference system of the map." ) );
790  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_ellipsoid" ), QCoreApplication::translate( "variable_help", "Acronym of the ellipsoid of the coordinate reference system of the map." ) );
791  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_proj4" ), QCoreApplication::translate( "variable_help", "Proj4 definition of the coordinate reference system of the map." ) );
792  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_wkt" ), QCoreApplication::translate( "variable_help", "WKT definition of the coordinate reference system of the map." ) );
793  sVariableHelpTexts()->insert( QStringLiteral( "map_layer_ids" ), QCoreApplication::translate( "variable_help", "List of map layer IDs visible in the map." ) );
794  sVariableHelpTexts()->insert( QStringLiteral( "map_layers" ), QCoreApplication::translate( "variable_help", "List of map layers visible in the map." ) );
795 
796  sVariableHelpTexts()->insert( QStringLiteral( "map_start_time" ), QCoreApplication::translate( "variable_help", "Start of the map's temporal time range (as a datetime value)" ) );
797  sVariableHelpTexts()->insert( QStringLiteral( "map_end_time" ), QCoreApplication::translate( "variable_help", "End of the map's temporal time range (as a datetime value)" ) );
798  sVariableHelpTexts()->insert( QStringLiteral( "map_interval" ), QCoreApplication::translate( "variable_help", "Duration of the map's temporal time range (as an interval value)" ) );
799 
800  sVariableHelpTexts()->insert( QStringLiteral( "frame_rate" ), QCoreApplication::translate( "variable_help", "Number of frames per second during animation playback" ) );
801  sVariableHelpTexts()->insert( QStringLiteral( "frame_number" ), QCoreApplication::translate( "variable_help", "Current frame number during animation playback" ) );
802  sVariableHelpTexts()->insert( QStringLiteral( "frame_duration" ), QCoreApplication::translate( "variable_help", "Temporal duration of each animation frame (as an interval value)" ) );
803  sVariableHelpTexts()->insert( QStringLiteral( "animation_start_time" ), QCoreApplication::translate( "variable_help", "Start of the animation's overall temporal time range (as a datetime value)" ) );
804  sVariableHelpTexts()->insert( QStringLiteral( "animation_end_time" ), QCoreApplication::translate( "variable_help", "End of the animation's overall temporal time range (as a datetime value)" ) );
805  sVariableHelpTexts()->insert( QStringLiteral( "animation_interval" ), QCoreApplication::translate( "variable_help", "Duration of the animation's overall temporal time range (as an interval value)" ) );
806 
807  // vector tile layer variables
808  sVariableHelpTexts()->insert( QStringLiteral( "zoom_level" ), QCoreApplication::translate( "variable_help", "Zoom level of the tile that is being rendered (derived from the current map scale). Normally in interval [0, 20]." ) );
809  sVariableHelpTexts()->insert( QStringLiteral( "vector_tile_zoom" ), QCoreApplication::translate( "variable_help", "Exact zoom level of the tile that is being rendered (derived from the current map scale). Normally in interval [0, 20]. Unlike @zoom_level, this variable is a floating point value which can be used to interpolated values between two integer zoom levels." ) );
810 
811  sVariableHelpTexts()->insert( QStringLiteral( "row_number" ), QCoreApplication::translate( "variable_help", "Stores the number of the current row." ) );
812  sVariableHelpTexts()->insert( QStringLiteral( "grid_number" ), QCoreApplication::translate( "variable_help", "Current grid annotation value." ) );
813  sVariableHelpTexts()->insert( QStringLiteral( "grid_axis" ), QCoreApplication::translate( "variable_help", "Current grid annotation axis (e.g., 'x' for longitude, 'y' for latitude)." ) );
814  sVariableHelpTexts()->insert( QStringLiteral( "column_number" ), QCoreApplication::translate( "variable_help", "Stores the number of the current column." ) );
815 
816  // map canvas item variables
817  sVariableHelpTexts()->insert( QStringLiteral( "canvas_cursor_point" ), QCoreApplication::translate( "variable_help", "Last cursor position on the canvas in the project's geographical coordinates." ) );
818 
819  // legend canvas item variables
820  sVariableHelpTexts()->insert( QStringLiteral( "legend_title" ), QCoreApplication::translate( "variable_help", "Title of the legend." ) );
821  sVariableHelpTexts()->insert( QStringLiteral( "legend_column_count" ), QCoreApplication::translate( "variable_help", "Number of column in the legend." ) );
822  sVariableHelpTexts()->insert( QStringLiteral( "legend_split_layers" ), QCoreApplication::translate( "variable_help", "Boolean indicating if layers can be split in the legend." ) );
823  sVariableHelpTexts()->insert( QStringLiteral( "legend_wrap_string" ), QCoreApplication::translate( "variable_help", "Characters used to wrap the legend text." ) );
824  sVariableHelpTexts()->insert( QStringLiteral( "legend_filter_by_map" ), QCoreApplication::translate( "variable_help", "Boolean indicating if the content of the legend is filtered by the map." ) );
825  sVariableHelpTexts()->insert( QStringLiteral( "legend_filter_out_atlas" ), QCoreApplication::translate( "variable_help", "Boolean indicating if the Atlas is filtered out of the legend." ) );
826 
827  // scalebar rendering
828  sVariableHelpTexts()->insert( QStringLiteral( "scale_value" ), QCoreApplication::translate( "variable_help", "Current scale bar distance value." ) );
829 
830  // map tool capture variables
831  sVariableHelpTexts()->insert( QStringLiteral( "snapping_results" ), QCoreApplication::translate( "variable_help",
832  "<p>An array with an item for each snapped point.</p>"
833  "<p>Each item is a map with the following keys:</p>"
834  "<dl>"
835  "<dt>valid</dt><dd>Boolean that indicates if the snapping result is valid</dd>"
836  "<dt>layer</dt><dd>The layer on which the snapped feature is</dd>"
837  "<dt>feature_id</dt><dd>The feature id of the snapped feature</dd>"
838  "<dt>vertex_index</dt><dd>The index of the snapped vertex</dd>"
839  "<dt>distance</dt><dd>The distance between the mouse cursor and the snapped point at the time of snapping</dd>"
840  "</dl>" ) );
841 
842 
843  //symbol variables
844  sVariableHelpTexts()->insert( QStringLiteral( "geometry_part_count" ), QCoreApplication::translate( "variable_help", "Number of parts in rendered feature's geometry." ) );
845  sVariableHelpTexts()->insert( QStringLiteral( "geometry_part_num" ), QCoreApplication::translate( "variable_help", "Current geometry part number for feature being rendered." ) );
846  sVariableHelpTexts()->insert( QStringLiteral( "geometry_ring_num" ), QCoreApplication::translate( "variable_help", "Current geometry ring number for feature being rendered (for polygon features only). The exterior ring has a value of 0." ) );
847  sVariableHelpTexts()->insert( QStringLiteral( "geometry_point_count" ), QCoreApplication::translate( "variable_help", "Number of points in the rendered geometry's part. It is only meaningful for line geometries and for symbol layers that set this variable." ) );
848  sVariableHelpTexts()->insert( QStringLiteral( "geometry_point_num" ), QCoreApplication::translate( "variable_help", "Current point number in the rendered geometry's part. It is only meaningful for line geometries and for symbol layers that set this variable." ) );
849 
850  sVariableHelpTexts()->insert( QStringLiteral( "symbol_color" ), QCoreApplication::translate( "symbol_color", "Color of symbol used to render the feature." ) );
851  sVariableHelpTexts()->insert( QStringLiteral( "symbol_angle" ), QCoreApplication::translate( "symbol_angle", "Angle of symbol used to render the feature (valid for marker symbols only)." ) );
852  sVariableHelpTexts()->insert( QStringLiteral( "symbol_layer_count" ), QCoreApplication::translate( "symbol_layer_count", "Total number of symbol layers in the symbol." ) );
853  sVariableHelpTexts()->insert( QStringLiteral( "symbol_layer_index" ), QCoreApplication::translate( "symbol_layer_index", "Current symbol layer index." ) );
854  sVariableHelpTexts()->insert( QStringLiteral( "symbol_marker_row" ), QCoreApplication::translate( "symbol_marker_row", "Row number for marker (valid for point pattern fills only)." ) );
855  sVariableHelpTexts()->insert( QStringLiteral( "symbol_marker_column" ), QCoreApplication::translate( "symbol_marker_column", "Column number for marker (valid for point pattern fills only)." ) );
856 
857  sVariableHelpTexts()->insert( QStringLiteral( "symbol_label" ), QCoreApplication::translate( "symbol_label", "Label for the symbol (either a user defined label or the default autogenerated label)." ) );
858  sVariableHelpTexts()->insert( QStringLiteral( "symbol_id" ), QCoreApplication::translate( "symbol_id", "Internal ID of the symbol." ) );
859  sVariableHelpTexts()->insert( QStringLiteral( "symbol_count" ), QCoreApplication::translate( "symbol_count", "Total number of features represented by the symbol." ) );
860 
861  //cluster variables
862  sVariableHelpTexts()->insert( QStringLiteral( "cluster_color" ), QCoreApplication::translate( "cluster_color", "Color of symbols within a cluster, or NULL if symbols have mixed colors." ) );
863  sVariableHelpTexts()->insert( QStringLiteral( "cluster_size" ), QCoreApplication::translate( "cluster_size", "Number of symbols contained within a cluster." ) );
864 
865  //processing variables
866  sVariableHelpTexts()->insert( QStringLiteral( "algorithm_id" ), QCoreApplication::translate( "algorithm_id", "Unique ID for algorithm." ) );
867  sVariableHelpTexts()->insert( QStringLiteral( "model_path" ), QCoreApplication::translate( "variable_help", "Full path (including file name) of current model (or project path if model is embedded in a project)." ) );
868  sVariableHelpTexts()->insert( QStringLiteral( "model_folder" ), QCoreApplication::translate( "variable_help", "Folder containing current model (or project folder if model is embedded in a project)." ) );
869  sVariableHelpTexts()->insert( QStringLiteral( "model_name" ), QCoreApplication::translate( "variable_help", "Name of current model." ) );
870  sVariableHelpTexts()->insert( QStringLiteral( "model_group" ), QCoreApplication::translate( "variable_help", "Group for current model." ) );
871  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_minx" ), QCoreApplication::translate( "fullextent_minx", "Minimum x-value from full canvas extent (including all layers)." ) );
872  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_miny" ), QCoreApplication::translate( "fullextent_miny", "Minimum y-value from full canvas extent (including all layers)." ) );
873  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_maxx" ), QCoreApplication::translate( "fullextent_maxx", "Maximum x-value from full canvas extent (including all layers)." ) );
874  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_maxy" ), QCoreApplication::translate( "fullextent_maxy", "Maximum y-value from full canvas extent (including all layers)." ) );
875 
876  //provider notification
877  sVariableHelpTexts()->insert( QStringLiteral( "notification_message" ), QCoreApplication::translate( "notification_message", "Content of the notification message sent by the provider (available only for actions triggered by provider notifications)." ) );
878 
879  //form context variable
880  sVariableHelpTexts()->insert( QStringLiteral( "current_geometry" ), QCoreApplication::translate( "current_geometry", "Represents the geometry of the feature currently being edited in the form or the table row. Can be used in a form/row context to filter the related features." ) );
881  sVariableHelpTexts()->insert( QStringLiteral( "current_feature" ), QCoreApplication::translate( "current_feature", "Represents the feature currently being edited in the form or the table row. Can be used in a form/row context to filter the related features." ) );
882 
883  //parent form context variable
884  sVariableHelpTexts()->insert( QStringLiteral( "current_parent_geometry" ), QCoreApplication::translate( "current_parent_geometry",
885  "Only usable in an embedded form context, "
886  "represents the geometry of the feature currently being edited in the parent form.\n"
887  "Can be used in a form/row context to filter the related features using a value "
888  "from the feature currently edited in the parent form, to make sure that the filter "
889  "still works with standalone forms it is recommended to wrap this variable in a "
890  "'coalesce()'." ) );
891  sVariableHelpTexts()->insert( QStringLiteral( "current_parent_feature" ), QCoreApplication::translate( "current_parent_feature",
892  "Only usable in an embedded form context, "
893  "represents the feature currently being edited in the parent form.\n"
894  "Can be used in a form/row context to filter the related features using a value "
895  "from the feature currently edited in the parent form, to make sure that the filter "
896  "still works with standalone forms it is recommended to wrap this variable in a "
897  "'coalesce()'." ) );
898 
899  //form variable
900  sVariableHelpTexts()->insert( QStringLiteral( "form_mode" ), QCoreApplication::translate( "form_mode", "What the form is used for, like AddFeatureMode, SingleEditMode, MultiEditMode, SearchMode, AggregateSearchMode or IdentifyMode as string." ) );
901 }
902 
903 
904 bool QgsExpression::addVariableHelpText( const QString name, const QString &description )
905 {
906  if ( sVariableHelpTexts()->contains( name ) )
907  {
908  return false;
909  }
910  sVariableHelpTexts()->insert( name, description );
911  return true;
912 }
913 
914 QString QgsExpression::variableHelpText( const QString &variableName )
915 {
916  QgsExpression::initVariableHelp();
917  return sVariableHelpTexts()->value( variableName, QString() );
918 }
919 
920 QString QgsExpression::formatVariableHelp( const QString &description, bool showValue, const QVariant &value )
921 {
922  QString text = !description.isEmpty() ? QStringLiteral( "<p>%1</p>" ).arg( description ) : QString();
923  if ( showValue )
924  {
925  QString valueString;
926  if ( !value.isValid() )
927  {
928  valueString = QCoreApplication::translate( "variable_help", "not set" );
929  }
930  else
931  {
932  valueString = QStringLiteral( "<b>%1</b>" ).arg( formatPreviewString( value ) );
933  }
934  text.append( QCoreApplication::translate( "variable_help", "<p>Current value: %1</p>" ).arg( valueString ) );
935  }
936  return text;
937 }
938 
939 QString QgsExpression::group( const QString &name )
940 {
941  if ( sGroups()->isEmpty() )
942  {
943  sGroups()->insert( QStringLiteral( "Aggregates" ), tr( "Aggregates" ) );
944  sGroups()->insert( QStringLiteral( "Arrays" ), tr( "Arrays" ) );
945  sGroups()->insert( QStringLiteral( "Color" ), tr( "Color" ) );
946  sGroups()->insert( QStringLiteral( "Conditionals" ), tr( "Conditionals" ) );
947  sGroups()->insert( QStringLiteral( "Conversions" ), tr( "Conversions" ) );
948  sGroups()->insert( QStringLiteral( "Date and Time" ), tr( "Date and Time" ) );
949  sGroups()->insert( QStringLiteral( "Fields and Values" ), tr( "Fields and Values" ) );
950  sGroups()->insert( QStringLiteral( "Files and Paths" ), tr( "Files and Paths" ) );
951  sGroups()->insert( QStringLiteral( "Fuzzy Matching" ), tr( "Fuzzy Matching" ) );
952  sGroups()->insert( QStringLiteral( "General" ), tr( "General" ) );
953  sGroups()->insert( QStringLiteral( "GeometryGroup" ), tr( "Geometry" ) );
954  sGroups()->insert( QStringLiteral( "Map Layers" ), tr( "Map Layers" ) );
955  sGroups()->insert( QStringLiteral( "Maps" ), tr( "Maps" ) );
956  sGroups()->insert( QStringLiteral( "Math" ), tr( "Math" ) );
957  sGroups()->insert( QStringLiteral( "Operators" ), tr( "Operators" ) );
958  sGroups()->insert( QStringLiteral( "Rasters" ), tr( "Rasters" ) );
959  sGroups()->insert( QStringLiteral( "Record and Attributes" ), tr( "Record and Attributes" ) );
960  sGroups()->insert( QStringLiteral( "String" ), tr( "String" ) );
961  sGroups()->insert( QStringLiteral( "Variables" ), tr( "Variables" ) );
962  sGroups()->insert( QStringLiteral( "Recent (%1)" ), tr( "Recent (%1)" ) );
963  sGroups()->insert( QStringLiteral( "UserGroup" ), tr( "User expressions" ) );
964  }
965 
966  //return the translated name for this group. If group does not
967  //have a translated name in the gGroups hash, return the name
968  //unchanged
969  return sGroups()->value( name, name );
970 }
971 
972 QString QgsExpression::formatPreviewString( const QVariant &value, const bool htmlOutput, int maximumPreviewLength )
973 {
974  const QString startToken = htmlOutput ? QStringLiteral( "<i>&lt;" ) : QStringLiteral( "<" );
975  const QString endToken = htmlOutput ? QStringLiteral( "&gt;</i>" ) : QStringLiteral( ">" );
976 
977  if ( value.canConvert<QgsGeometry>() )
978  {
979  //result is a geometry
980  QgsGeometry geom = value.value<QgsGeometry>();
981  if ( geom.isNull() )
982  return startToken + tr( "empty geometry" ) + endToken;
983  else
984  return startToken + tr( "geometry: %1" ).arg( QgsWkbTypes::displayString( geom.constGet()->wkbType() ) )
985  + endToken;
986  }
987  else if ( value.value< QgsWeakMapLayerPointer >().data() )
988  {
989  return startToken + tr( "map layer" ) + endToken;
990  }
991  else if ( !value.isValid() )
992  {
993  return htmlOutput ? tr( "<i>NULL</i>" ) : QString();
994  }
995  else if ( value.canConvert< QgsFeature >() )
996  {
997  //result is a feature
998  QgsFeature feat = value.value<QgsFeature>();
999  return startToken + tr( "feature: %1" ).arg( feat.id() ) + endToken;
1000  }
1001  else if ( value.canConvert< QgsInterval >() )
1002  {
1003  QgsInterval interval = value.value<QgsInterval>();
1004  if ( interval.days() > 1 )
1005  {
1006  return startToken + tr( "interval: %1 days" ).arg( interval.days() ) + endToken;
1007  }
1008  else if ( interval.hours() > 1 )
1009  {
1010  return startToken + tr( "interval: %1 hours" ).arg( interval.hours() ) + endToken;
1011  }
1012  else if ( interval.minutes() > 1 )
1013  {
1014  return startToken + tr( "interval: %1 minutes" ).arg( interval.minutes() ) + endToken;
1015  }
1016  else
1017  {
1018  return startToken + tr( "interval: %1 seconds" ).arg( interval.seconds() ) + endToken;
1019  }
1020  }
1021  else if ( value.canConvert< QgsGradientColorRamp >() )
1022  {
1023  return startToken + tr( "gradient ramp" ) + endToken;
1024  }
1025  else if ( value.type() == QVariant::Date )
1026  {
1027  const QDate dt = value.toDate();
1028  return startToken + tr( "date: %1" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd" ) ) ) + endToken;
1029  }
1030  else if ( value.type() == QVariant::Time )
1031  {
1032  const QTime tm = value.toTime();
1033  return startToken + tr( "time: %1" ).arg( tm.toString( QStringLiteral( "hh:mm:ss" ) ) ) + endToken;
1034  }
1035  else if ( value.type() == QVariant::DateTime )
1036  {
1037  const QDateTime dt = value.toDateTime();
1038  return startToken + tr( "datetime: %1 (%2)" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd hh:mm:ss" ) ), dt.timeZoneAbbreviation() ) + endToken;
1039  }
1040  else if ( value.type() == QVariant::String )
1041  {
1042  const QString previewString = value.toString();
1043  if ( previewString.length() > maximumPreviewLength + 3 )
1044  {
1045  return tr( "'%1…'" ).arg( previewString.left( maximumPreviewLength ) );
1046  }
1047  else
1048  {
1049  return '\'' + previewString + '\'';
1050  }
1051  }
1052  else if ( value.type() == QVariant::Map )
1053  {
1054  QString mapStr = QStringLiteral( "{" );
1055  const QVariantMap map = value.toMap();
1056  QString separator;
1057  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1058  {
1059  mapStr.append( separator );
1060  if ( separator.isEmpty() )
1061  separator = QStringLiteral( "," );
1062 
1063  mapStr.append( QStringLiteral( " '%1': %2" ).arg( it.key(), formatPreviewString( it.value(), htmlOutput ) ) );
1064  if ( mapStr.length() > maximumPreviewLength - 3 )
1065  {
1066  mapStr = tr( "%1…" ).arg( mapStr.left( maximumPreviewLength - 2 ) );
1067  break;
1068  }
1069  }
1070  if ( !map.empty() )
1071  mapStr += QLatin1Char( ' ' );
1072  mapStr += QLatin1Char( '}' );
1073  return mapStr;
1074  }
1075  else if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
1076  {
1077  QString listStr = QStringLiteral( "[" );
1078  const QVariantList list = value.toList();
1079  QString separator;
1080  for ( const QVariant &arrayValue : list )
1081  {
1082  listStr.append( separator );
1083  if ( separator.isEmpty() )
1084  separator = QStringLiteral( "," );
1085 
1086  listStr.append( " " );
1087  listStr.append( formatPreviewString( arrayValue, htmlOutput ) );
1088  if ( listStr.length() > maximumPreviewLength - 3 )
1089  {
1090  listStr = QString( tr( "%1…" ) ).arg( listStr.left( maximumPreviewLength - 2 ) );
1091  break;
1092  }
1093  }
1094  if ( !list.empty() )
1095  listStr += QLatin1Char( ' ' );
1096  listStr += QLatin1Char( ']' );
1097  return listStr;
1098  }
1099  else if ( value.type() == QVariant::Int ||
1100  value.type() == QVariant::UInt ||
1101  value.type() == QVariant::LongLong ||
1102  value.type() == QVariant::ULongLong ||
1103  value.type() == QVariant::Double ||
1104  // Qt madness with QMetaType::Float :/
1105  value.type() == static_cast<QVariant::Type>( QMetaType::Float ) )
1106  {
1107  return QgsExpressionUtils::toLocalizedString( value );
1108  }
1109  else
1110  {
1111  QString str { value.toString() };
1112  if ( str.length() > maximumPreviewLength - 3 )
1113  {
1114  str = tr( "%1…" ).arg( str.left( maximumPreviewLength - 2 ) );
1115  }
1116  return str;
1117  }
1118 }
1119 
1120 QString QgsExpression::createFieldEqualityExpression( const QString &fieldName, const QVariant &value )
1121 {
1122  QString expr;
1123 
1124  if ( value.isNull() )
1125  expr = QStringLiteral( "%1 IS NULL" ).arg( quotedColumnRef( fieldName ) );
1126  else
1127  expr = QStringLiteral( "%1 = %2" ).arg( quotedColumnRef( fieldName ), quotedValue( value ) );
1128 
1129  return expr;
1130 }
1131 
1132 bool QgsExpression::isFieldEqualityExpression( const QString &expression, QString &field, QVariant &value )
1133 {
1135 
1136  if ( !e.rootNode() )
1137  return false;
1138 
1139  if ( const QgsExpressionNodeBinaryOperator *binOp = dynamic_cast<const QgsExpressionNodeBinaryOperator *>( e.rootNode() ) )
1140  {
1141  if ( binOp->op() == QgsExpressionNodeBinaryOperator::boEQ )
1142  {
1143  const QgsExpressionNodeColumnRef *columnRef = dynamic_cast<const QgsExpressionNodeColumnRef *>( binOp->opLeft() );
1144  const QgsExpressionNodeLiteral *literal = dynamic_cast<const QgsExpressionNodeLiteral *>( binOp->opRight() );
1145  if ( columnRef && literal )
1146  {
1147  field = columnRef->name();
1148  value = literal->value();
1149  return true;
1150  }
1151  }
1152  }
1153  return false;
1154 }
1155 
1156 bool QgsExpression::attemptReduceToInClause( const QStringList &expressions, QString &result )
1157 {
1158  if ( expressions.empty() )
1159  return false;
1160 
1161  QString inField;
1162  bool first = true;
1163  QStringList values;
1164  for ( const QString &expression : expressions )
1165  {
1166  QString field;
1167  QVariant value;
1169  {
1170  if ( first )
1171  {
1172  inField = field;
1173  first = false;
1174  }
1175  else if ( field != inField )
1176  {
1177  return false;
1178  }
1179  values << QgsExpression::quotedValue( value );
1180  }
1181  else
1182  {
1183  // we also allow reducing similar 'field IN (...)' expressions!
1185 
1186  if ( !e.rootNode() )
1187  return false;
1188 
1189  if ( const QgsExpressionNodeInOperator *inOp = dynamic_cast<const QgsExpressionNodeInOperator *>( e.rootNode() ) )
1190  {
1191  if ( inOp->isNotIn() )
1192  return false;
1193 
1194  const QgsExpressionNodeColumnRef *columnRef = dynamic_cast<const QgsExpressionNodeColumnRef *>( inOp->node() );
1195  if ( !columnRef )
1196  return false;
1197 
1198  if ( first )
1199  {
1200  inField = columnRef->name();
1201  first = false;
1202  }
1203  else if ( columnRef->name() != inField )
1204  {
1205  return false;
1206  }
1207 
1208  if ( QgsExpressionNode::NodeList *nodeList = inOp->list() )
1209  {
1210  const QList<QgsExpressionNode *> nodes = nodeList->list();
1211  for ( const QgsExpressionNode *node : nodes )
1212  {
1213  const QgsExpressionNodeLiteral *literal = dynamic_cast<const QgsExpressionNodeLiteral *>( node );
1214  if ( !literal )
1215  return false;
1216 
1217  values << QgsExpression::quotedValue( literal->value() );
1218  }
1219  }
1220  }
1221  // Collect ORs
1222  else if ( const QgsExpressionNodeBinaryOperator *orOp = dynamic_cast<const QgsExpressionNodeBinaryOperator *>( e.rootNode() ) )
1223  {
1224 
1225  // OR Collector function: returns a possibly empty list of the left and right operands of an OR expression
1226  std::function<QStringList( QgsExpressionNode *, QgsExpressionNode * )> collectOrs = [ &collectOrs ]( QgsExpressionNode * opLeft, QgsExpressionNode * opRight ) -> QStringList
1227  {
1228  QStringList orParts;
1229  if ( const QgsExpressionNodeBinaryOperator *leftOrOp = dynamic_cast<const QgsExpressionNodeBinaryOperator *>( opLeft ) )
1230  {
1231  if ( leftOrOp->op( ) == QgsExpressionNodeBinaryOperator::BinaryOperator::boOr )
1232  {
1233  orParts.append( collectOrs( leftOrOp->opLeft(), leftOrOp->opRight() ) );
1234  }
1235  else
1236  {
1237  orParts.append( leftOrOp->dump() );
1238  }
1239  }
1240  else if ( const QgsExpressionNodeInOperator *leftInOp = dynamic_cast<const QgsExpressionNodeInOperator *>( opLeft ) )
1241  {
1242  orParts.append( leftInOp->dump() );
1243  }
1244  else
1245  {
1246  return {};
1247  }
1248 
1249  if ( const QgsExpressionNodeBinaryOperator *rightOrOp = dynamic_cast<const QgsExpressionNodeBinaryOperator *>( opRight ) )
1250  {
1251  if ( rightOrOp->op( ) == QgsExpressionNodeBinaryOperator::BinaryOperator::boOr )
1252  {
1253  orParts.append( collectOrs( rightOrOp->opLeft(), rightOrOp->opRight() ) );
1254  }
1255  else
1256  {
1257  orParts.append( rightOrOp->dump() );
1258  }
1259  }
1260  else if ( const QgsExpressionNodeInOperator *rightInOp = dynamic_cast<const QgsExpressionNodeInOperator *>( opRight ) )
1261  {
1262  orParts.append( rightInOp->dump() );
1263  }
1264  else
1265  {
1266  return {};
1267  }
1268 
1269  return orParts;
1270  };
1271 
1272  if ( orOp->op( ) == QgsExpressionNodeBinaryOperator::BinaryOperator::boOr )
1273  {
1274  // Try to collect all OR conditions
1275  const QStringList orParts = collectOrs( orOp->opLeft(), orOp->opRight() );
1276  if ( orParts.isEmpty() )
1277  {
1278  return false;
1279  }
1280  else
1281  {
1282  QString orPartsResult;
1283  if ( attemptReduceToInClause( orParts, orPartsResult ) )
1284  {
1285  // Need to check if the IN field is correct,
1286  QgsExpression inExp { orPartsResult };
1287  if ( ! inExp.rootNode() )
1288  {
1289  return false;
1290  }
1291 
1292  if ( const QgsExpressionNodeInOperator *inOpInner = dynamic_cast<const QgsExpressionNodeInOperator *>( inExp.rootNode() ) )
1293  {
1294  if ( inOpInner->node()->nodeType() != QgsExpressionNode::NodeType::ntColumnRef || inOpInner->node()->referencedColumns().size() < 1 )
1295  {
1296  return false;
1297  }
1298 
1299  const QString innerInfield { inOpInner->node()->referencedColumns().values().first() };
1300 
1301  if ( first )
1302  {
1303  inField = innerInfield;
1304  first = false;
1305  }
1306 
1307  if ( innerInfield != inField )
1308  {
1309  return false;
1310  }
1311  else
1312  {
1313  const auto constInnerValuesList { inOpInner->list()->list() };
1314  for ( const auto &innerInValueNode : std::as_const( constInnerValuesList ) )
1315  {
1316  values.append( innerInValueNode->dump() );
1317  }
1318  }
1319 
1320  }
1321  else
1322  {
1323  return false;
1324  }
1325  }
1326  else
1327  {
1328  return false;
1329  }
1330  }
1331  }
1332  else
1333  {
1334  return false;
1335  }
1336  }
1337  else
1338  {
1339  return false;
1340  }
1341  }
1342  }
1343  result = QStringLiteral( "%1 IN (%2)" ).arg( quotedColumnRef( inField ), values.join( ',' ) );
1344  return true;
1345 }
1346 
1348 {
1349  return d->mRootNode;
1350 }
1351 
1353 {
1354  return d->mRootNode && d->mRootNode->nodeType() == QgsExpressionNode::ntColumnRef;
1355 }
1356 
1357 int QgsExpression::expressionToLayerFieldIndex( const QString &expression, const QgsVectorLayer *layer )
1358 {
1359  if ( !layer )
1360  return -1;
1361 
1362  // easy check first -- lookup field directly.
1363  int attrIndex = layer->fields().lookupField( expression.trimmed() );
1364  if ( attrIndex >= 0 )
1365  return attrIndex;
1366 
1367  // may still be a simple field expression, just one which is enclosed in "" or similar
1368  QgsExpression candidate( expression );
1369  if ( candidate.isField() )
1370  {
1371  const QString fieldName = qgis::down_cast<const QgsExpressionNodeColumnRef *>( candidate.rootNode() )->name();
1372  return layer->fields().lookupField( fieldName );
1373  }
1374  return -1;
1375 }
1376 
1377 QList<const QgsExpressionNode *> QgsExpression::nodes() const
1378 {
1379  if ( !d->mRootNode )
1380  return QList<const QgsExpressionNode *>();
1381 
1382  return d->mRootNode->nodes();
1383 }
1384 
1385 
1386 
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
A binary expression operator, which operates on two values.
An expression node which takes it value from a feature's field.
QString name() const
The name of the column.
An expression node for value IN or NOT IN clauses.
An expression node for literal values.
QVariant value() const
The value of the literal.
A list of expression nodes.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
Abstract base class for all nodes that can appear in an expression.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
static const QList< QgsExpressionFunction * > & Functions()
QgsExpression & operator=(const QgsExpression &other)
Create a copy of this expression.
QString expression() const
Returns the original, unmodified expression string.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
void setExpression(const QString &expression)
Set the expression string, will reset the whole internal structure.
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
static double evaluateToDouble(const QString &text, double fallbackValue)
Attempts to evaluate a text string as an expression to a resultant double value.
static int functionCount()
Returns the number of functions defined in the parser.
QList< const QgsExpressionNode * > nodes() const
Returns a list of all nodes which are used in this expression.
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes)
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
static bool isFunctionName(const QString &name)
tells whether the identifier is a name of existing function
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
static QString variableHelpText(const QString &variableName)
Returns the help text for a specified variable.
QList< QgsExpression::ParserError > parserErrors() const
Returns parser error details including location of error.
QString evalErrorString() const
Returns evaluation error.
bool operator==(const QgsExpression &other) const
Compares two expressions.
QString parserErrorString() const
Returns parser error.
static QString formatPreviewString(const QVariant &value, bool htmlOutput=true, int maximumPreviewLength=60)
Formats an expression result for friendly display to the user.
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
void setDistanceUnits(QgsUnitTypes::DistanceUnit unit)
Sets the desired distance units for calculations involving geomCalculator(), e.g.,...
static QStringList tags(const QString &name)
Returns a string list of search tags for a specified function.
bool isField() const
Checks whether an expression consists only of a single field reference.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
QgsUnitTypes::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g.,...
QgsExpression()
Create an empty expression.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
QString dump() const
Returns an expression string, constructed from the internal abstract syntax tree.
static QString helpText(QString name)
Returns the help text for a specified function.
static bool isFieldEqualityExpression(const QString &expression, QString &field, QVariant &value)
Returns true if the given expression is a simple "field=value" type expression.
QSet< QString > referencedFunctions() const
Returns a list of the names of all functions which are used in this expression.
static QString group(const QString &group)
Returns the translated name for a function group.
QgsUnitTypes::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions)
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
void setAreaUnits(QgsUnitTypes::AreaUnit unit)
Sets the desired areal units for calculations involving geomCalculator(), e.g., "$area".
void setGeomCalculator(const QgsDistanceArea *calc)
Sets the geometry calculator used for distance and area calculations in expressions.
const QgsExpressionNode * rootNode() const
Returns the root node of the expression.
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
static QString formatVariableHelp(const QString &description, bool showValue=true, const QVariant &value=QVariant())
Returns formatted help text for a variable.
static int expressionToLayerFieldIndex(const QString &expression, const QgsVectorLayer *layer)
Attempts to resolve an expression to a field index from the given layer.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
static bool attemptReduceToInClause(const QStringList &expressions, QString &result)
Attempts to reduce a list of expressions to a single "field IN (val1, val2, ... )" type expression.
bool isValid() const
Checks if this expression is valid.
static bool addVariableHelpText(const QString name, const QString &description)
Adds a help string for a custom variable.
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
QSet< int > referencedAttributeIndexes(const QgsFields &fields) const
Returns a list of field name indexes obtained from the provided fields.
static bool checkExpression(const QString &text, const QgsExpressionContext *context, QString &errorMessage)
Tests whether a string is a valid expression.
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
Definition: qgsfields.cpp:371
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Q_GADGET bool isNull
Definition: qgsgeometry.h:127
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorramp.h:153
A representation of the interval between two datetime values.
Definition: qgsinterval.h:42
double days() const
Returns the interval duration in days.
double seconds() const
Returns the interval duration in seconds.
Definition: qgsinterval.h:236
double hours() const
Returns the interval duration in hours.
double minutes() const
Returns the interval duration in minutes.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:467
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:68
@ DistanceUnknownUnit
Unknown distance unit.
Definition: qgsunittypes.h:78
static Q_INVOKABLE QgsUnitTypes::DistanceUnit stringToDistanceUnit(const QString &string, bool *ok=nullptr)
Converts a translated string to a distance unit.
static Q_INVOKABLE QgsUnitTypes::AreaUnit stringToAreaUnit(const QString &string, bool *ok=nullptr)
Converts a translated string to an areal unit.
AreaUnit
Units of area.
Definition: qgsunittypes.h:94
@ AreaUnknownUnit
Unknown areal unit.
Definition: qgsunittypes.h:106
Represents a vector layer which manages a vector based data sets.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
static QString displayString(Type type) SIP_HOLDGIL
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
#define str(x)
Definition: qgis.cpp:37
CONSTLATIN1STRING geoNone()
Constant that holds the string representation for "No ellips/No CRS".
Definition: qgis.h:1660
QMap< QString, QString > QgsStringMap
Definition: qgis.h:1703
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QgsExpressionNode * parseExpression(const QString &str, QString &parserErrorMsg, QList< QgsExpression::ParserError > &parserErrors)
HelpTextHash & functionHelpTexts()
const QgsField & field
Definition: qgsfield.h:463
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:2130