30#include <Qsci/qscilexer.h>
32#include "moc_qgssqlcomposerdialog.cpp"
34using namespace Qt::StringLiterals;
45 connect( mTablesCombo,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsSQLComposerDialog::mTablesCombo_currentIndexChanged );
46 connect( mColumnsCombo,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsSQLComposerDialog::mColumnsCombo_currentIndexChanged );
47 connect( mSpatialPredicatesCombo,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsSQLComposerDialog::mSpatialPredicatesCombo_currentIndexChanged );
48 connect( mFunctionsCombo,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsSQLComposerDialog::mFunctionsCombo_currentIndexChanged );
49 connect( mOperatorsCombo,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsSQLComposerDialog::mOperatorsCombo_currentIndexChanged );
50 connect( mAddJoinButton, &QPushButton::clicked,
this, &QgsSQLComposerDialog::mAddJoinButton_clicked );
51 connect( mRemoveJoinButton, &QPushButton::clicked,
this, &QgsSQLComposerDialog::mRemoveJoinButton_clicked );
52 connect( mTableJoins, &QTableWidget::itemSelectionChanged,
this, &QgsSQLComposerDialog::mTableJoins_itemSelectionChanged );
54 mQueryEdit->setWrapMode( QsciScintilla::WrapWord );
55 mQueryEdit->installEventFilter(
this );
56 mColumnsEditor->installEventFilter(
this );
57 mTablesEditor->installEventFilter(
this );
58 mTableJoins->installEventFilter(
this );
59 mWhereEditor->installEventFilter(
this );
60 mOrderEditor->installEventFilter(
this );
61 mTablesCombo->view()->installEventFilter(
this );
64 connect( mButtonBox->button( QDialogButtonBox::Reset ), &QAbstractButton::clicked,
this, &QgsSQLComposerDialog::reset );
66 connect( mQueryEdit, &QsciScintilla::textChanged,
this, &QgsSQLComposerDialog::splitSQLIntoFields );
67 connect( mColumnsEditor, &QTextEdit::textChanged,
this, &QgsSQLComposerDialog::buildSQLFromFields );
68 connect( mTablesEditor, &QLineEdit::textChanged,
this, &QgsSQLComposerDialog::buildSQLFromFields );
69 connect( mWhereEditor, &QTextEdit::textChanged,
this, &QgsSQLComposerDialog::buildSQLFromFields );
70 connect( mOrderEditor, &QTextEdit::textChanged,
this, &QgsSQLComposerDialog::buildSQLFromFields );
71 connect( mTableJoins, &QTableWidget::cellChanged,
this, &QgsSQLComposerDialog::buildSQLFromFields );
72 connect( mButtonBox, &QDialogButtonBox::helpRequested,
this, &QgsSQLComposerDialog::showHelp );
75 baseList << u
"SELECT"_s;
76 baseList << u
"FROM"_s;
77 baseList << u
"JOIN"_s;
79 baseList << u
"USING"_s;
80 baseList << u
"WHERE"_s;
85 baseList << u
"NULL"_s;
86 baseList << u
"LIKE"_s;
87 baseList << u
"ORDER"_s;
91 QStringList operatorsList;
92 operatorsList << u
"AND"_s;
93 operatorsList << u
"OR"_s;
94 operatorsList << u
"NOT"_s;
95 operatorsList << u
"="_s;
96 operatorsList << u
"<"_s;
97 operatorsList << u
"<="_s;
98 operatorsList << u
">"_s;
99 operatorsList << u
">="_s;
100 operatorsList << u
"<>"_s;
101 operatorsList << u
"BETWEEN"_s;
102 operatorsList << u
"NOT BETWEEN"_s;
103 operatorsList << u
"IS"_s;
104 operatorsList << u
"IS NOT"_s;
105 operatorsList << u
"IN"_s;
106 operatorsList << u
"LIKE"_s;
109 mAggregatesCombo->hide();
110 mFunctionsCombo->hide();
111 mSpatialPredicatesCombo->hide();
112 mStringFunctionsCombo->hide();
114 delete mPageColumnsValues;
115 mPageColumnsValues =
nullptr;
117 mRemoveJoinButton->setEnabled(
false );
119 mTableJoins->setRowCount( 0 );
120 mTableJoins->setItem( 0, 0,
new QTableWidgetItem( QString() ) );
121 mTableJoins->setItem( 0, 1,
new QTableWidgetItem( QString() ) );
129 delete mQueryEdit->lexer()->apis();
130 mQueryEdit->lexer()->setAPIs(
nullptr );
135 if ( event->type() == QEvent::FocusIn )
137 if ( obj == mTablesCombo->view() )
138 lastSearchedText.clear();
140 mFocusedObject = obj;
144 if ( event->type() == QEvent::KeyPress && obj == mTablesCombo->view() )
146 QString currentString = ( ( QKeyEvent * ) event )->text();
147 if ( !currentString.isEmpty() && ( ( currentString[0] >=
'a' && currentString[0] <=
'z' ) || ( currentString[0] >=
'A' && currentString[0] <=
'Z' ) || ( currentString[0] >=
'0' && currentString[0] <=
'9' ) || currentString[0] ==
':' || currentString[0] ==
'_' || currentString[0] ==
' ' || currentString[0] ==
'(' || currentString[0] ==
')' ) )
151 const int attemptCount = ( lastSearchedText.isEmpty() ) ? 1 : 2;
152 for (
int attempt = 0; attempt < attemptCount; attempt++ )
155 lastSearchedText += currentString;
157 lastSearchedText = currentString;
162 int iBestCandidate = 0;
163 int idxInTextOfBestCandidate = 1000;
164 for (
int i = 1; i < mTablesCombo->count(); i++ )
166 const int idxInText = mTablesCombo->itemText( i ).indexOf( lastSearchedText, Qt::CaseInsensitive );
167 if ( idxInText >= 0 && idxInText < idxInTextOfBestCandidate )
170 idxInTextOfBestCandidate = idxInText;
173 if ( iBestCandidate > 0 )
175 mTablesCombo->view()->setCurrentIndex( mTablesCombo->model()->index( 0, 0 ).sibling( iBestCandidate, 0 ) );
179 lastSearchedText.clear();
183 return QDialog::eventFilter( obj, event );
188 mTableSelectedCallback = tableSelectedCallback;
193 mSQLValidatorCallback = sqlValidatorCallback;
199 mQueryEdit->setText(
sql );
204 return mQueryEdit->text();
207void QgsSQLComposerDialog::accept()
209 if ( mSQLValidatorCallback )
211 QString errorMsg, warningMsg;
212 if ( !mSQLValidatorCallback->
isValid(
sql(), errorMsg, warningMsg ) )
214 if ( errorMsg.isEmpty() )
215 errorMsg = tr(
"An error occurred during evaluation of the SQL statement." );
216 QMessageBox::critical(
this, tr(
"SQL Evaluation" ), errorMsg );
219 if ( !warningMsg.isEmpty() )
221 QMessageBox::warning(
this, tr(
"SQL Evaluation" ), warningMsg );
226 mLayer->setSubsetString(
sql() );
231void QgsSQLComposerDialog::buildSQLFromFields()
233 if ( mAlreadyModifyingFields )
235 mAlreadyModifyingFields =
true;
236 QString
sql( u
"SELECT "_s );
238 sql +=
"DISTINCT "_L1;
239 sql += mColumnsEditor->toPlainText();
241 sql += mTablesEditor->text();
243 const int rows = mTableJoins->rowCount();
244 for (
int i = 0; i < rows; i++ )
246 QTableWidgetItem *itemTable = mTableJoins->item( i, 0 );
247 QTableWidgetItem *itemOn = mTableJoins->item( i, 1 );
248 if ( itemTable && !itemTable->text().isEmpty() && itemOn && !itemOn->text().isEmpty() )
251 sql += itemTable->text();
253 sql += itemOn->text();
257 if ( !mWhereEditor->toPlainText().isEmpty() )
260 sql += mWhereEditor->toPlainText();
262 if ( !mOrderEditor->toPlainText().isEmpty() )
264 sql +=
" ORDER BY "_L1;
265 sql += mOrderEditor->toPlainText();
267 mQueryEdit->setText(
sql );
269 mAlreadyModifyingFields =
false;
272void QgsSQLComposerDialog::splitSQLIntoFields()
274 if ( mAlreadyModifyingFields )
276 const QgsSQLStatement
sql( mQueryEdit->text() );
277 if (
sql.hasParserError() )
279 const QgsSQLStatement::NodeSelect *nodeSelect =
dynamic_cast<const QgsSQLStatement::NodeSelect *
>(
sql.rootNode() );
283 const QList<QgsSQLStatement::NodeSelectedColumn *> columns = nodeSelect->
columns();
285 const auto constColumns = columns;
286 for ( QgsSQLStatement::NodeSelectedColumn *column : constColumns )
288 if ( !columnText.isEmpty() )
289 columnText +=
", "_L1;
290 columnText += column->dump();
293 const QList<QgsSQLStatement::NodeTableDef *> tables = nodeSelect->
tables();
295 const auto constTables = tables;
296 for ( QgsSQLStatement::NodeTableDef *table : constTables )
298 if ( !tablesText.isEmpty() )
299 tablesText +=
", "_L1;
301 tablesText += table->dump();
305 QgsSQLStatement::Node *where = nodeSelect->
where();
307 whereText = where->
dump();
310 const QList<QgsSQLStatement::NodeColumnSorted *> orderColumns = nodeSelect->
orderBy();
311 const auto constOrderColumns = orderColumns;
312 for ( QgsSQLStatement::NodeColumnSorted *column : constOrderColumns )
314 if ( !orderText.isEmpty() )
315 orderText +=
", "_L1;
316 orderText += column->dump();
319 const QList<QgsSQLStatement::NodeJoin *> joins = nodeSelect->
joins();
321 mAlreadyModifyingFields =
true;
322 mColumnsEditor->setPlainText( columnText );
323 mTablesEditor->setText( tablesText );
324 mWhereEditor->setPlainText( whereText );
325 mOrderEditor->setPlainText( orderText );
327 mTableJoins->setRowCount( joins.size() + 1 );
329 const auto constJoins = joins;
330 for ( QgsSQLStatement::NodeJoin *join : constJoins )
333 mTableJoins->setItem( iRow, 0,
new QTableWidgetItem( join->tableDef()->dump() ) );
334 if ( join->onExpr() )
335 mTableJoins->setItem( iRow, 1,
new QTableWidgetItem( join->onExpr()->dump() ) );
337 mTableJoins->setItem( iRow, 1,
new QTableWidgetItem( QString() ) );
340 mTableJoins->setItem( iRow, 0,
new QTableWidgetItem( QString() ) );
341 mTableJoins->setItem( iRow, 1,
new QTableWidgetItem( QString() ) );
343 mAlreadyModifyingFields =
false;
348 const auto constList = list;
349 for (
const QString &name : constList )
350 mapTableEntryTextToName[name] = name;
351 mTablesCombo->addItems( list );
357 QStringList listCombo;
359 const auto constListNameTitle = listNameTitle;
362 listApi << pair.first;
363 QString entryText( pair.first );
364 if ( !pair.second.isEmpty() && pair.second != pair.first )
366 if ( pair.second.size() < 40 )
367 entryText +=
" (" + pair.second +
")";
369 entryText +=
" (" + pair.second.mid( 0, 20 ) + QChar( 0x2026 ) + pair.second.mid( pair.second.size() - 20 ) +
")";
371 listCombo << entryText;
372 mapTableEntryTextToName[entryText] = pair.first;
374 mTablesCombo->addItems( listCombo );
380 QList<PairNameType> listPair;
381 const auto constList = list;
382 for (
const QString &name : constList )
387static QString sanitizeType( QString type )
389 if ( type.startsWith(
"xs:"_L1 ) )
390 return type.mid( 3 );
391 if ( type.startsWith(
"xsd:"_L1 ) )
392 return type.mid( 4 );
393 if ( type ==
"gml:AbstractGeometryType"_L1 )
394 return u
"geometry"_s;
400 mAlreadySelectedTables.insert( tableName );
401 if ( mColumnsCombo->count() > 1 )
402 mColumnsCombo->insertSeparator( mColumnsCombo->count() );
404 QStringList listCombo;
406 const auto constList = list;
409 listApi << pair.first;
410 QString entryText( pair.first );
411 if ( !pair.second.isEmpty() )
413 entryText +=
" (" + sanitizeType( pair.second ) +
")";
415 listCombo << entryText;
416 mapColumnEntryTextToName[entryText] = pair.first;
418 mColumnsCombo->addItems( listCombo );
425 mOperatorsCombo->addItems( list );
433 return QObject::tr(
"%1 to %n argument(s)",
nullptr, f.
maxArgs ).arg( f.
minArgs );
440 return QObject::tr(
"%n argument(s)",
nullptr, f.
minArgs );
444 return QObject::tr(
"%n argument(s) or more",
nullptr, f.
minArgs );
450void QgsSQLComposerDialog::getFunctionList(
const QList<Function> &list, QStringList &listApi, QStringList &listCombo, QMap<QString, QString> &mapEntryTextToName )
452 const auto constList = list;
453 for (
const Function &f : constList )
456 QString entryText( f.
name );
465 entryText +=
", "_L1;
473 const QString sanitizedType( sanitizeType( f.
argumentList[i].type ) );
476 entryText +=
": "_L1;
477 entryText += sanitizedType;
483 if ( entryText.size() > 60 )
487 entryText += getFunctionAbbridgedParameters( f );
492 entryText += getFunctionAbbridgedParameters( f );
496 entryText +=
": " + sanitizeType( f.
returnType );
497 listCombo << entryText;
498 mapEntryTextToName[entryText] = f.
name +
"(";
504 QList<Function> listFunction;
505 const auto constList = list;
506 for (
const QString &name : constList )
518 QStringList listCombo;
519 getFunctionList( list, listApi, listCombo, mapSpatialPredicateEntryTextToName );
520 mSpatialPredicatesCombo->addItems( listCombo );
521 mSpatialPredicatesCombo->show();
527 QList<Function> listFunction;
528 const auto constList = list;
529 for (
const QString &name : constList )
541 QStringList listCombo;
542 getFunctionList( list, listApi, listCombo, mapFunctionEntryTextToName );
543 mFunctionsCombo->addItems( listCombo );
544 mFunctionsCombo->show();
548void QgsSQLComposerDialog::loadTableColumns(
const QString &table )
550 if ( mTableSelectedCallback )
552 if ( !mAlreadySelectedTables.contains( table ) )
555 mAlreadySelectedTables.insert( table );
560static void resetCombo( QComboBox *combo )
567 QMetaObject::invokeMethod( combo,
"setCurrentIndex", Qt::QueuedConnection, Q_ARG(
int, 0 ) );
570void QgsSQLComposerDialog::mTablesCombo_currentIndexChanged(
int )
572 const int index = mTablesCombo->currentIndex();
575 QObject *obj = mFocusedObject;
576 const QString newText = mapTableEntryTextToName[mTablesCombo->currentText()];
577 loadTableColumns( newText );
578 if ( obj == mTablesEditor )
580 const QString currentText = mTablesEditor->text();
581 if ( currentText.isEmpty() )
582 mTablesEditor->setText( newText );
584 mTablesEditor->setText( currentText +
", " + newText );
586 else if ( obj == mTableJoins )
588 if ( mTableJoins->selectedItems().size() == 1 )
590 mTableJoins->selectedItems().at( 0 )->setText( newText );
593 else if ( obj == mWhereEditor )
595 mWhereEditor->insertPlainText( newText );
597 else if ( obj == mOrderEditor )
599 mOrderEditor->insertPlainText( newText );
601 else if ( obj == mQueryEdit )
603 mQueryEdit->insertText( newText );
605 resetCombo( mTablesCombo );
608void QgsSQLComposerDialog::mColumnsCombo_currentIndexChanged(
int )
610 const int index = mColumnsCombo->currentIndex();
613 QObject *obj = mFocusedObject;
614 const QString newText = mapColumnEntryTextToName[mColumnsCombo->currentText()];
615 if ( obj == mColumnsEditor )
617 const QString currentText = mColumnsEditor->toPlainText();
618 if ( currentText.isEmpty() )
619 mColumnsEditor->insertPlainText( newText );
621 mColumnsEditor->insertPlainText(
",\n" + newText );
623 else if ( obj == mTableJoins )
625 if ( mTableJoins->selectedItems().size() == 1 && mTableJoins->selectedItems().at( 0 )->column() == 1 )
627 const QString currentText( mTableJoins->selectedItems().at( 0 )->text() );
628 if ( !currentText.isEmpty() && !currentText.contains(
"="_L1 ) )
629 mTableJoins->selectedItems().at( 0 )->setText( currentText +
" = " + newText );
631 mTableJoins->selectedItems().at( 0 )->setText( currentText + newText );
634 else if ( obj == mWhereEditor )
636 mWhereEditor->insertPlainText( newText );
638 else if ( obj == mOrderEditor )
640 mOrderEditor->insertPlainText( newText );
642 else if ( obj == mQueryEdit )
644 mQueryEdit->insertText( newText );
646 resetCombo( mColumnsCombo );
649void QgsSQLComposerDialog::mFunctionsCombo_currentIndexChanged(
int )
651 functionCurrentIndexChanged( mFunctionsCombo, mapFunctionEntryTextToName );
654void QgsSQLComposerDialog::mSpatialPredicatesCombo_currentIndexChanged(
int )
656 functionCurrentIndexChanged( mSpatialPredicatesCombo, mapSpatialPredicateEntryTextToName );
659void QgsSQLComposerDialog::functionCurrentIndexChanged( QComboBox *combo,
const QMap<QString, QString> &mapEntryTextToName )
661 const int index = combo->currentIndex();
664 QObject *obj = mFocusedObject;
665 const QString newText = mapEntryTextToName[combo->currentText()];
666 if ( obj == mColumnsEditor )
668 mColumnsEditor->insertPlainText( newText );
670 else if ( obj == mWhereEditor )
672 mWhereEditor->insertPlainText( newText );
674 else if ( obj == mQueryEdit )
676 mQueryEdit->insertText( newText );
681void QgsSQLComposerDialog::mOperatorsCombo_currentIndexChanged(
int )
683 const int index = mOperatorsCombo->currentIndex();
686 QObject *obj = mFocusedObject;
687 const QString newText = mOperatorsCombo->currentText();
688 if ( obj == mColumnsEditor )
690 mColumnsEditor->insertPlainText( newText );
692 else if ( obj == mWhereEditor )
694 mWhereEditor->insertPlainText( newText );
696 else if ( obj == mTableJoins )
698 if ( mTableJoins->selectedItems().size() == 1 && mTableJoins->selectedItems().at( 0 )->column() == 1 )
700 const QString currentText( mTableJoins->selectedItems().at( 0 )->text() );
701 mTableJoins->selectedItems().at( 0 )->setText( currentText + newText );
704 else if ( obj == mQueryEdit )
706 mQueryEdit->insertText( newText );
708 resetCombo( mOperatorsCombo );
711void QgsSQLComposerDialog::mAddJoinButton_clicked()
713 int insertRow = mTableJoins->currentRow();
714 const int rowCount = mTableJoins->rowCount();
716 insertRow = rowCount;
717 mTableJoins->setRowCount( rowCount + 1 );
718 for (
int row = rowCount; row > insertRow + 1; row-- )
720 mTableJoins->setItem( row, 0, mTableJoins->takeItem( row - 1, 0 ) );
721 mTableJoins->setItem( row, 1, mTableJoins->takeItem( row - 1, 1 ) );
723 mTableJoins->setItem( ( insertRow == rowCount ) ? insertRow : insertRow + 1, 0,
new QTableWidgetItem( QString() ) );
724 mTableJoins->setItem( ( insertRow == rowCount ) ? insertRow : insertRow + 1, 1,
new QTableWidgetItem( QString() ) );
727void QgsSQLComposerDialog::mRemoveJoinButton_clicked()
729 int row = mTableJoins->currentRow();
732 const int rowCount = mTableJoins->rowCount();
733 for ( ; row < rowCount - 1; row++ )
735 mTableJoins->setItem( row, 0, mTableJoins->takeItem( row + 1, 0 ) );
736 mTableJoins->setItem( row, 1, mTableJoins->takeItem( row + 1, 1 ) );
738 mTableJoins->setRowCount( rowCount - 1 );
740 buildSQLFromFields();
743void QgsSQLComposerDialog::reset()
745 mQueryEdit->setText( mResetSql );
748void QgsSQLComposerDialog::mTableJoins_itemSelectionChanged()
750 mRemoveJoinButton->setEnabled( mTableJoins->selectedItems().size() == 1 );
757 delete mQueryEdit->lexer()->apis();
758 QsciAPIs *apis =
new QsciAPIs( mQueryEdit->lexer() );
760 const auto constMApiList = mApiList;
761 for (
const QString &str : constMApiList )
767 mQueryEdit->lexer()->setAPIs( apis );
772 mJoinsLabels->setVisible( on );
773 mTableJoins->setVisible( on );
774 mAddJoinButton->setVisible( on );
775 mRemoveJoinButton->setVisible( on );
776 mTablesCombo->setVisible( on );
778 QString mainTypenameFormatted;
779 if ( !mainTypename.isEmpty() )
780 mainTypenameFormatted =
"(" + mainTypename +
")";
781 mQueryEdit->setToolTip( tr(
"This is the SQL query editor. The SQL statement can select data from several tables, \n"
782 "but it must compulsory include the main typename %1 in the selected tables, \n"
783 "and only the geometry column of the main typename can be used as the geometry column of the resulting layer." )
784 .arg( mainTypenameFormatted ) );
787void QgsSQLComposerDialog::showHelp()
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Callback to do validation check on dialog validation.
virtual bool isValid(const QString &sql, QString &errorReason, QString &warningMsg)=0
method should return true if the SQL is valid. Otherwise return false and set the errorReason
Callback to do actions on table selection.
virtual void tableSelected(const QString &name)=0
method called when a table is selected
bool eventFilter(QObject *obj, QEvent *event) override
QgsSQLComposerDialog(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
constructor
void addSpatialPredicates(const QStringList &list)
add a list of spatial predicates
void setSQLValidatorCallback(SQLValidatorCallback *sqlValidatorCallback)
Set a callback that will be called when the OK button is pushed.
void setTableSelectedCallback(TableSelectedCallback *tableSelectedCallback)
Set a callback that will be called when a new table is selected, so that new column names can be adde...
QPair< QString, QString > PairNameType
pair (name, type)
void addOperators(const QStringList &list)
add a list of operators
~QgsSQLComposerDialog() override
void addColumnNames(const QStringList &list, const QString &tableName)
add a list of column names
void setSupportMultipleTables(bool bMultipleTables, const QString &mainTypename=QString())
Sets if multiple tables/joins are supported. Default is false.
void addApis(const QStringList &list)
add a list of API for autocompletion
void setSql(const QString &sql)
initialize the SQL statement
void addFunctions(const QStringList &list)
add a list of functions
void addTableNames(const QStringList &list)
add a list of table names
QPair< QString, QString > PairNameTitle
pair (name, title)
QString sql() const
Gets the SQL statement.
QList< QgsSQLStatement::NodeColumnSorted * > orderBy() const
Returns the list of order by columns.
QList< QgsSQLStatement::NodeSelectedColumn * > columns() const
Returns the list of columns.
bool distinct() const
Returns if the SELECT is DISTINCT.
QList< QgsSQLStatement::NodeJoin * > joins() const
Returns the list of joins.
QgsSQLStatement::Node * where() const
Returns the where clause.
QList< QgsSQLStatement::NodeTableDef * > tables() const
Returns the list of tables.
virtual QString dump() const =0
Abstract virtual dump method.
static QString quotedIdentifierIfNeeded(const QString &name)
Returns a quoted column reference (in double quotes) if needed, or otherwise the original string.
QgsSubsetStringEditorInterface(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
Constructor.
Represents a vector layer which manages a vector based dataset.
description of server functions
QString returnType
Returns type, or empty if unknown.
int maxArgs
maximum number of argument (or -1 if unknown)
int minArgs
minimum number of argument (or -1 if unknown)
QList< Argument > argumentList
list of arguments. May be empty despite minArgs > 0