123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- // Qt includes
- #include <QHash>
- #include <QStringList>
- #include <QTextStream>
- #include <QDebug>
- // CTK includes
- #include "ctkCommandLineParser.h"
- namespace
- {
- class CommandLineParserArgumentDescription
- {
- public:
- CommandLineParserArgumentDescription(
- const QString& longArg, const QString& shortArg, QVariant::Type type,
- const QString& argHelp, const QVariant& defaultValue, bool ignoreRest)
- : LongArg(longArg), ShortArg(shortArg), ArgHelp(argHelp),
- IgnoreRest(ignoreRest), NumberOfParametersToProcess(0),
- Value(type)
- {
- if (defaultValue.isValid())
- {
- Value = defaultValue;
- }
- switch (type)
- {
- case QVariant::String:
- {
- NumberOfParametersToProcess = 1;
- RegularExpression = ".*";
- }
- break;
- case QVariant::Bool:
- {
- NumberOfParametersToProcess = 0;
- RegularExpression = "";
- }
- break;
- case QVariant::StringList:
- {
- NumberOfParametersToProcess = -1;
- RegularExpression = ".*";
- }
- break;
- case QVariant::Int:
- {
- NumberOfParametersToProcess = 1;
- RegularExpression = "-?[0-9]+";
- ExactMatchFailedMessage = "A negative or positive integer is expected.";
- }
- break;
- default:
- ExactMatchFailedMessage = QString("Type %1 not supported.").arg(static_cast<int>(type));
- }
- }
- ~CommandLineParserArgumentDescription(){}
- bool addParameter(const QString& value);
- QString helpText(int fieldWidth, const char charPad);
- QString LongArg;
- QString ShortArg;
- QString ArgHelp;
- bool IgnoreRest;
- int NumberOfParametersToProcess;
- QString RegularExpression;
- QString ExactMatchFailedMessage;
- QVariant Value;
- };
- // --------------------------------------------------------------------------
- bool CommandLineParserArgumentDescription::addParameter(const QString& value)
- {
- if (!RegularExpression.isEmpty())
- {
- // Validate value
- QRegExp regexp(this->RegularExpression);
- if (!regexp.exactMatch(value))
- {
- return false;
- }
- }
- switch (Value.type())
- {
- case QVariant::String:
- {
- Value.setValue(value);
- }
- break;
- case QVariant::Bool:
- {
- Value.setValue(!QString::compare(value, "true", Qt::CaseInsensitive));
- }
- break;
- case QVariant::StringList:
- {
- if (Value.isNull())
- {
- QStringList list;
- list << value;
- Value.setValue(list);
- }
- else
- {
- QStringList list = Value.toStringList();
- list << value;
- Value.setValue(list);
- }
- }
- break;
- case QVariant::Int:
- {
- Value.setValue(value.toInt());
- }
- break;
- default:
- return false;
- }
- return true;
- }
- // --------------------------------------------------------------------------
- QString CommandLineParserArgumentDescription::helpText(int fieldWidth, const char charPad)
- {
- QString text;
- QTextStream stream(&text);
- stream.setFieldAlignment(QTextStream::AlignLeft);
- stream.setPadChar(charPad);
- if (this->LongArg.isEmpty() && !this->ArgHelp.isEmpty())
- {
- stream.setFieldWidth(fieldWidth);
- }
- if (!this->ShortArg.isEmpty())
- {
- stream << QString(" %1").arg(this->ShortArg);
- if(!this->LongArg.isEmpty())
- {
- stream << "\n";
- }
- }
- if (!this->LongArg.isEmpty())
- {
- if(!this->ArgHelp.isEmpty())
- {
- stream.setFieldWidth(fieldWidth);
- }
- stream << QString(" %1").arg(this->LongArg);
- }
- stream.setFieldWidth(0);
- stream << this->ArgHelp << "\n";
- return text;
- }
- }
- // --------------------------------------------------------------------------
- // ctkCommandLineParser::ctkInternal class
- // --------------------------------------------------------------------------
- class ctkCommandLineParser::ctkInternal
- {
- public:
- ctkInternal():Debug(false),FieldWidth(0){}
- ~ctkInternal() { qDeleteAll(ArgumentDescriptionList); }
-
- CommandLineParserArgumentDescription* argumentDescription(const QString& argument);
-
- QList<CommandLineParserArgumentDescription*> ArgumentDescriptionList;
- QHash<QString, CommandLineParserArgumentDescription*> ArgNameToArgumentDescriptionMap;
-
- QStringList UnparsedArguments;
- QStringList ProcessedArguments;
- QString ErrorString;
- bool Debug;
- int FieldWidth;
- };
- // --------------------------------------------------------------------------
- // ctkCommandLineParser::ctkInternal methods
- // --------------------------------------------------------------------------
- CommandLineParserArgumentDescription*
- ctkCommandLineParser::ctkInternal::argumentDescription(const QString& argument)
- {
- if (this->ArgNameToArgumentDescriptionMap.contains(argument))
- {
- return this->ArgNameToArgumentDescriptionMap[argument];
- }
- return 0;
- }
- // --------------------------------------------------------------------------
- // ctkCommandLineParser methods
- // --------------------------------------------------------------------------
- ctkCommandLineParser::ctkCommandLineParser()
- {
- this->Internal = new ctkInternal();
- }
- // --------------------------------------------------------------------------
- ctkCommandLineParser::~ctkCommandLineParser()
- {
- delete this->Internal;
- }
- // --------------------------------------------------------------------------
- QHash<QString, QVariant> ctkCommandLineParser::parseArguments(const QStringList& arguments,
- bool* ok)
- {
- // Reset
- this->Internal->UnparsedArguments.clear();
- this->Internal->ProcessedArguments.clear();
- this->Internal->ErrorString.clear();
- bool ignoreRest = false;
- CommandLineParserArgumentDescription * currentArgDesc = 0;
- for(int i = 1; i < arguments.size(); ++i)
- {
- QString argument = arguments.at(i);
- if (this->Internal->Debug) { qDebug() << "Processing" << argument; }
- // should argument be ignored ?
- if (ignoreRest)
- {
- this->Internal->UnparsedArguments << argument;
- continue;
- }
- // Skip if argument has already been parsed ...
- if (this->Internal->ProcessedArguments.contains(argument))
- {
- qDebug() << "Skipping argument" << argument << " - Already processed !";
- continue;
- }
- // Retrieve corresponding argument description
- currentArgDesc = this->Internal->argumentDescription(argument);
- // Is there a corresponding argument description ?
- if (currentArgDesc)
- {
- this->Internal->ProcessedArguments << currentArgDesc->ShortArg << currentArgDesc->LongArg;
- int numberOfParametersToProcess = currentArgDesc->NumberOfParametersToProcess;
- ignoreRest = currentArgDesc->IgnoreRest;
- // Is the number of parameters associated with the argument being processed known ?
- if (numberOfParametersToProcess == 0)
- {
- currentArgDesc->addParameter("true");
- }
- else if (numberOfParametersToProcess > 0)
- {
- QString missingParameterError =
- "Argument %1 has %2 value(s) associated whereas exacly %3 are expected.";
- for(int j=1; j <= numberOfParametersToProcess; ++j)
- {
- if (i + j >= arguments.size())
- {
- this->Internal->ErrorString =
- missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess);
- if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
- if (ok) *ok = false;
- return QHash<QString, QVariant>();
- }
- QString parameter = arguments.at(i + j);
- if (this->Internal->Debug)
- {
- qDebug() << "Processing parameter" << j << ", value:" << parameter;
- }
- if (this->argumentAdded(parameter))
- {
- this->Internal->ErrorString =
- missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess);
- if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
- if (ok) *ok = false;
- return QHash<QString, QVariant>();
- }
- if (!currentArgDesc->addParameter(parameter))
- {
- this->Internal->ErrorString = QString(
- "Value(s) associated with argument %1 are incorrect. %2").
- arg(argument).arg(currentArgDesc->ExactMatchFailedMessage);
- if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
- if (ok) *ok = false;
- return QHash<QString, QVariant>();
- }
- }
- // Update main loop increment
- i = i + numberOfParametersToProcess;
- }
- else if (numberOfParametersToProcess == -1)
- {
- if (this->Internal->Debug)
- {
- qDebug() << "Proccessing StringList ...";
- }
- int j = 1;
- while(j + i < arguments.size())
- {
- if (this->argumentAdded(arguments.at(j + i)))
- {
- if (this->Internal->Debug)
- {
- qDebug() << "No more parameter for" << argument;
- }
- break;
- }
- QString parameter = arguments.at(j + i);
- if (this->Internal->Debug)
- {
- qDebug() << "Processing parameter" << j << ", value:" << parameter;
- }
- if (!currentArgDesc->addParameter(parameter))
- {
- this->Internal->ErrorString = QString(
- "Value(s) associated with argument %1 are incorrect. %2").
- arg(argument).arg(currentArgDesc->ExactMatchFailedMessage);
- if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
- if (ok) *ok = false;
- return QHash<QString, QVariant>();
- }
- j++;
- }
- // Update main loop increment
- i = i + j;
- }
- }
- else
- {
- this->Internal->UnparsedArguments << argument;
- }
- }
- if (ok) *ok = true;
- QHash<QString, QVariant> parsedArguments;
- QListIterator<CommandLineParserArgumentDescription*> it(this->Internal->ArgumentDescriptionList);
- while (it.hasNext())
- {
- QString key;
- CommandLineParserArgumentDescription* desc = it.next();
- if (!desc->LongArg.isEmpty())
- {
- key = desc->LongArg;
- }
- else
- {
- key = desc->ShortArg;
- }
- parsedArguments.insert(key, desc->Value);
- }
- return parsedArguments;
- }
- // -------------------------------------------------------------------------
- QString ctkCommandLineParser::errorString()
- {
- return this->Internal->ErrorString;
- }
- // -------------------------------------------------------------------------
- const QStringList& ctkCommandLineParser::unparsedArguments()
- {
- return this->Internal->UnparsedArguments;
- }
- // --------------------------------------------------------------------------
- void ctkCommandLineParser::addArgument(const QString& longarg, const QString& shortarg,
- QVariant::Type type, const QString& argHelp,
- const QVariant& defaultValue, bool ignoreRest)
- {
- Q_ASSERT(!defaultValue.isValid() || defaultValue.type() == type);
- /* Make sure it's not already added */
- bool added = this->Internal->ArgNameToArgumentDescriptionMap.contains(longarg);
- Q_ASSERT(!added);
- if (added) { return; }
- added = this->Internal->ArgNameToArgumentDescriptionMap.contains(shortarg);
- Q_ASSERT(!added);
- if (added) { return; }
- CommandLineParserArgumentDescription* argDesc =
- new CommandLineParserArgumentDescription(longarg, shortarg, type,
- argHelp, defaultValue, ignoreRest);
- Q_ASSERT(!(longarg.isEmpty() && shortarg.isEmpty()));
- if (longarg.isEmpty() && shortarg.isEmpty()) { return; }
- if (!longarg.isEmpty())
- {
- this->Internal->ArgNameToArgumentDescriptionMap[longarg] = argDesc;
- int argWidth = longarg.length() + 7;
- if (argWidth > this->Internal->FieldWidth)
- {
- this->Internal->FieldWidth = argWidth;
- }
- }
- if (!shortarg.isEmpty())
- {
- this->Internal->ArgNameToArgumentDescriptionMap[shortarg] = argDesc;
- int argWidth = shortarg.length() + 7;
- if (argWidth > this->Internal->FieldWidth)
- {
- this->Internal->FieldWidth = argWidth;
- }
- }
- this->Internal->ArgumentDescriptionList << argDesc;
- }
- // --------------------------------------------------------------------------
- bool ctkCommandLineParser::setExactMatchRegularExpression(
- const QString& argument, const QString& expression, const QString& exactMatchFailedMessage)
- {
- CommandLineParserArgumentDescription * argDesc =
- this->Internal->argumentDescription(argument);
- if (!argDesc)
- {
- return false;
- }
- if (argDesc->Value.type() == QVariant::Bool)
- {
- return false;
- }
- argDesc->RegularExpression = expression;
- argDesc->ExactMatchFailedMessage = exactMatchFailedMessage;
- return true;
- }
- // --------------------------------------------------------------------------
- QString ctkCommandLineParser::helpText(const char charPad)
- {
- QString text;
- QTextStream stream(&text);
- // Loop over argument descriptions
- foreach(CommandLineParserArgumentDescription* argDesc,
- this->Internal->ArgumentDescriptionList)
- {
- stream << argDesc->helpText(this->Internal->FieldWidth, charPad);
- }
- return text;
- }
- // --------------------------------------------------------------------------
- bool ctkCommandLineParser::argumentAdded(const QString& argument)
- {
- return this->Internal->ArgNameToArgumentDescriptionMap.contains(argument);
- }
- // --------------------------------------------------------------------------
- bool ctkCommandLineParser::argumentParsed(const QString& argument)
- {
- return this->Internal->ProcessedArguments.contains(argument);
- }
|