// Qt includes #include #include #include #include // CTK includes #include "ctkCommandLineParser.h" namespace { class CommandLineParserArgumentDescriptionBase { public: CommandLineParserArgumentDescriptionBase( const char* longArg, const char* shortArg, const QString& argHelp, bool ignoreRest) { this->LongArg = QLatin1String(longArg); this->ShortArg = QLatin1String(shortArg); this->ArgHelp = argHelp; this->IgnoreRest = ignoreRest; this->NumberOfParametersToProcess = 0; } virtual ~CommandLineParserArgumentDescriptionBase(){} virtual bool addParameter(const QString& value) = 0; QString helpText(int fieldWidth, const char charPad); QString LongArg; QString ShortArg; QString ArgHelp; bool IgnoreRest; int NumberOfParametersToProcess; QString RegularExpression; QString ExactMatchFailedMessage; QString ArgumentType; }; // -------------------------------------------------------------------------- QString CommandLineParserArgumentDescriptionBase::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; } #define CommandLineParserArgumentDescription_class(_NAME, _TYPE, \ _NUMBEROFPARAMTOPROCESS, \ _REGEXP, _EXACTMACTHERRORMSG) \ class CommandLineParser##_NAME##ArgumentDescription: \ public CommandLineParserArgumentDescriptionBase \ { \ public: \ CommandLineParser##_NAME##ArgumentDescription( \ const char* longArg, const char* shortArg, _TYPE * variable, \ const QString& argHelp, const _TYPE& defaultValue, \ bool ignoreRest): \ CommandLineParserArgumentDescriptionBase(longArg , shortArg, argHelp, ignoreRest) \ { \ this->Variable = variable; \ this->DefaultValue = defaultValue; \ this->NumberOfParametersToProcess = _NUMBEROFPARAMTOPROCESS; \ this->RegularExpression = _REGEXP; \ this->ArgumentType = #_TYPE; \ } \ virtual ~ CommandLineParser##_NAME##ArgumentDescription(){} \ virtual bool addParameter(const QString& value); \ _TYPE* Variable; \ _TYPE DefaultValue; \ }; CommandLineParserArgumentDescription_class(String, QString, 1, ".*", ""); CommandLineParserArgumentDescription_class(Boolean, bool, 0, "", ""); CommandLineParserArgumentDescription_class(StringList, QStringList, -1, ".*", ""); CommandLineParserArgumentDescription_class(Integer, int, 1, "-?[0-9]+", "A negative or positive integer is expected."); #undef CommandLineParserArgumentDescription_class // -------------------------------------------------------------------------- bool CommandLineParserStringArgumentDescription::addParameter(const QString& value) { // Validate value QRegExp regexp(this->RegularExpression); if (!regexp.exactMatch(value)) { return false; } (*this->Variable).clear(); (*this->Variable).append(value); return true; } // -------------------------------------------------------------------------- bool CommandLineParserBooleanArgumentDescription::addParameter(const QString& value) { *this->Variable = (value == "true"); return true; } // -------------------------------------------------------------------------- bool CommandLineParserStringListArgumentDescription::addParameter(const QString& value) { // Validate value QRegExp regexp(this->RegularExpression); if (!regexp.exactMatch(value)) { return false; } *this->Variable << value; return true; } // -------------------------------------------------------------------------- bool CommandLineParserIntegerArgumentDescription::addParameter(const QString& value) { // Validate value QRegExp regexp(this->RegularExpression); if (!regexp.exactMatch(value)) { return false; } *this->Variable = value.toInt(); return true; } } // -------------------------------------------------------------------------- // ctkCommandLineParser::ctkInternal class // -------------------------------------------------------------------------- class ctkCommandLineParser::ctkInternal { public: ctkInternal():Debug(false),FieldWidth(0){} CommandLineParserArgumentDescriptionBase* argumentDescription(const QString& argument); QList ArgumentDescriptionList; QHash ArgNameToArgumentDescriptionMap; #define ctkCommandLineParser_ctkInternal_declare_map(_NAME) \ QHash \ LongArgTo##_NAME##ArgumentDescriptionMap; \ QHash \ ShortArgTo##_NAME##ArgumentDescriptionMap; ctkCommandLineParser_ctkInternal_declare_map(String); ctkCommandLineParser_ctkInternal_declare_map(Boolean); ctkCommandLineParser_ctkInternal_declare_map(StringList); ctkCommandLineParser_ctkInternal_declare_map(Integer); #undef ctkCommandLineParser_ctkInternal_declare_map QStringList UnparsedArguments; QStringList ProcessedArguments; QString ErrorString; bool Debug; int FieldWidth; }; // -------------------------------------------------------------------------- // ctkCommandLineParser::ctkInternal methods // -------------------------------------------------------------------------- CommandLineParserArgumentDescriptionBase* 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; } // -------------------------------------------------------------------------- bool ctkCommandLineParser::parseArguments(const QStringList& arguments) { // Reset this->Internal->UnparsedArguments.clear(); this->Internal->ProcessedArguments.clear(); this->Internal->ErrorString.clear(); bool ignoreRest = false; CommandLineParserArgumentDescriptionBase * 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; } return false; } 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; } return false; } 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; } return false; } } // 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; } return false; } j++; } // Update main loop increment i = i + j; } } else { this->Internal->UnparsedArguments << argument; } } return true; } // ------------------------------------------------------------------------- QString ctkCommandLineParser::errorString() { return this->Internal->ErrorString; } // ------------------------------------------------------------------------- const QStringList& ctkCommandLineParser::unparsedArguments() { return this->Internal->UnparsedArguments; } // ------------------------------------------------------------------------- #define ctkCommandLineParser_addArgument_cxx_core(_NAME, _TYPE) \ /* Make sure it's not already added */ \ bool added = this->Internal->LongArgTo##_NAME##ArgumentDescriptionMap.contains(longarg); \ Q_ASSERT(!added); \ if (added) { return; } \ \ added = this->Internal->ShortArgTo##_NAME##ArgumentDescriptionMap.contains(shortarg); \ Q_ASSERT(!added); \ if (added) { return; } \ \ CommandLineParser##_NAME##ArgumentDescription * argDesc = \ new CommandLineParser##_NAME##ArgumentDescription(longarg, shortarg, var, \ argHelp, defaultValue, ignoreRest); \ \ Q_ASSERT(!(longarg == 0 && shortarg == 0)); \ if (longarg == 0 && shortarg == 0) { return; } \ if (longarg != 0) \ { \ this->Internal->LongArgTo##_NAME##ArgumentDescriptionMap[longarg] = argDesc; \ int argWidth = QString(longarg).length() + 7; \ if (argWidth > this->Internal->FieldWidth) \ { \ this->Internal->FieldWidth = argWidth; \ } \ } \ if (shortarg != 0) \ { \ this->Internal->ShortArgTo##_NAME##ArgumentDescriptionMap[shortarg] = argDesc; \ int argWidth = QString(shortarg).length() + 7; \ if (argWidth > this->Internal->FieldWidth) \ { \ this->Internal->FieldWidth = argWidth; \ } \ } \ this->Internal->ArgNameToArgumentDescriptionMap[longarg] = argDesc; \ this->Internal->ArgNameToArgumentDescriptionMap[shortarg] = argDesc; \ this->Internal->ArgumentDescriptionList << argDesc; // ------------------------------------------------------------------------- #define ctkCommandLineParser_addArgument_cxx(_NAME, _TYPE) \ void ctkCommandLineParser::add##_NAME##Argument(const char* longarg, \ const char* shortarg, _TYPE* var, const QString& argHelp, const _TYPE& defaultValue, \ bool ignoreRest) \ { \ ctkCommandLineParser_addArgument_cxx_core(_NAME, _TYPE); \ } // ------------------------------------------------------------------------- #define ctkCommandLineParser_addArgument_cxx_without_ignore_rest(_NAME, _TYPE) \ void ctkCommandLineParser::add##_NAME##Argument(const char* longarg, \ const char* shortarg, _TYPE* var, const QString& argHelp, const _TYPE& defaultValue) \ { \ bool ignoreRest = false; \ ctkCommandLineParser_addArgument_cxx_core(_NAME, _TYPE); \ } // -------------------------------------------------------------------------- ctkCommandLineParser_addArgument_cxx(String, QString); ctkCommandLineParser_addArgument_cxx(Boolean, bool); ctkCommandLineParser_addArgument_cxx_without_ignore_rest(StringList, QStringList); ctkCommandLineParser_addArgument_cxx(Integer, int); #undef ctkCommandLineParser_addArgument_cxx // -------------------------------------------------------------------------- bool ctkCommandLineParser::setExactMatchRegularExpression( const QString& argument, const QString& expression, const QString& exactMatchFailedMessage) { CommandLineParserArgumentDescriptionBase * argDesc = this->Internal->argumentDescription(argument); if (!argDesc) { return false; } if (argDesc->ArgumentType == "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(CommandLineParserArgumentDescriptionBase* 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); }