ctkCommandLineParser.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. // Qt includes
  2. #include <QHash>
  3. #include <QStringList>
  4. #include <QTextStream>
  5. #include <QDebug>
  6. // CTK includes
  7. #include "ctkCommandLineParser.h"
  8. namespace
  9. {
  10. class CommandLineParserArgumentDescriptionBase
  11. {
  12. public:
  13. CommandLineParserArgumentDescriptionBase(
  14. const char* longArg, const char* shortArg, const QString& argHelp, bool ignoreRest)
  15. {
  16. this->LongArg = QLatin1String(longArg);
  17. this->ShortArg = QLatin1String(shortArg);
  18. this->ArgHelp = argHelp;
  19. this->IgnoreRest = ignoreRest;
  20. this->NumberOfParametersToProcess = 0;
  21. }
  22. virtual ~CommandLineParserArgumentDescriptionBase(){}
  23. virtual bool addParameter(const QString& value) = 0;
  24. QString helpText(int fieldWidth, const char charPad);
  25. QString LongArg;
  26. QString ShortArg;
  27. QString ArgHelp;
  28. bool IgnoreRest;
  29. int NumberOfParametersToProcess;
  30. QString RegularExpression;
  31. QString ExactMatchFailedMessage;
  32. QString ArgumentType;
  33. };
  34. // --------------------------------------------------------------------------
  35. QString CommandLineParserArgumentDescriptionBase::helpText(int fieldWidth, const char charPad)
  36. {
  37. QString text;
  38. QTextStream stream(&text);
  39. stream.setFieldAlignment(QTextStream::AlignLeft);
  40. stream.setPadChar(charPad);
  41. if (this->LongArg.isEmpty() && !this->ArgHelp.isEmpty())
  42. {
  43. stream.setFieldWidth(fieldWidth);
  44. }
  45. if (!this->ShortArg.isEmpty())
  46. {
  47. stream << QString(" %1").arg(this->ShortArg);
  48. if(!this->LongArg.isEmpty())
  49. {
  50. stream << "\n";
  51. }
  52. }
  53. if (!this->LongArg.isEmpty())
  54. {
  55. if(!this->ArgHelp.isEmpty())
  56. {
  57. stream.setFieldWidth(fieldWidth);
  58. }
  59. stream << QString(" %1").arg(this->LongArg);
  60. }
  61. stream.setFieldWidth(0);
  62. stream << this->ArgHelp << "\n";
  63. return text;
  64. }
  65. #define CommandLineParserArgumentDescription_class(_NAME, _TYPE, \
  66. _NUMBEROFPARAMTOPROCESS, \
  67. _REGEXP, _EXACTMACTHERRORMSG) \
  68. class CommandLineParser##_NAME##ArgumentDescription: \
  69. public CommandLineParserArgumentDescriptionBase \
  70. { \
  71. public: \
  72. CommandLineParser##_NAME##ArgumentDescription( \
  73. const char* longArg, const char* shortArg, _TYPE * variable, \
  74. const QString& argHelp, const _TYPE& defaultValue, \
  75. bool ignoreRest): \
  76. CommandLineParserArgumentDescriptionBase(longArg , shortArg, argHelp, ignoreRest) \
  77. { \
  78. this->Variable = variable; \
  79. this->DefaultValue = defaultValue; \
  80. this->NumberOfParametersToProcess = _NUMBEROFPARAMTOPROCESS; \
  81. this->RegularExpression = _REGEXP; \
  82. this->ArgumentType = #_TYPE; \
  83. } \
  84. virtual ~ CommandLineParser##_NAME##ArgumentDescription(){} \
  85. virtual bool addParameter(const QString& value); \
  86. _TYPE* Variable; \
  87. _TYPE DefaultValue; \
  88. };
  89. CommandLineParserArgumentDescription_class(String, QString, 1, ".*", "");
  90. CommandLineParserArgumentDescription_class(Boolean, bool, 0, "", "");
  91. CommandLineParserArgumentDescription_class(StringList, QStringList, -1, ".*", "");
  92. CommandLineParserArgumentDescription_class(Integer, int, 1, "-?[0-9]+",
  93. "A negative or positive integer is expected.");
  94. #undef CommandLineParserArgumentDescription_class
  95. // --------------------------------------------------------------------------
  96. bool CommandLineParserStringArgumentDescription::addParameter(const QString& value)
  97. {
  98. // Validate value
  99. QRegExp regexp(this->RegularExpression);
  100. if (!regexp.exactMatch(value))
  101. {
  102. return false;
  103. }
  104. (*this->Variable).clear();
  105. (*this->Variable).append(value);
  106. return true;
  107. }
  108. // --------------------------------------------------------------------------
  109. bool CommandLineParserBooleanArgumentDescription::addParameter(const QString& value)
  110. {
  111. *this->Variable = (value == "true");
  112. return true;
  113. }
  114. // --------------------------------------------------------------------------
  115. bool CommandLineParserStringListArgumentDescription::addParameter(const QString& value)
  116. {
  117. // Validate value
  118. QRegExp regexp(this->RegularExpression);
  119. if (!regexp.exactMatch(value))
  120. {
  121. return false;
  122. }
  123. *this->Variable << value;
  124. return true;
  125. }
  126. // --------------------------------------------------------------------------
  127. bool CommandLineParserIntegerArgumentDescription::addParameter(const QString& value)
  128. {
  129. // Validate value
  130. QRegExp regexp(this->RegularExpression);
  131. if (!regexp.exactMatch(value))
  132. {
  133. return false;
  134. }
  135. *this->Variable = value.toInt();
  136. return true;
  137. }
  138. }
  139. // --------------------------------------------------------------------------
  140. // ctkCommandLineParser::ctkInternal class
  141. // --------------------------------------------------------------------------
  142. class ctkCommandLineParser::ctkInternal
  143. {
  144. public:
  145. ctkInternal():Debug(false),FieldWidth(0){}
  146. CommandLineParserArgumentDescriptionBase* argumentDescription(const QString& argument);
  147. QList<CommandLineParserArgumentDescriptionBase*> ArgumentDescriptionList;
  148. QHash<QString, CommandLineParserArgumentDescriptionBase*> ArgNameToArgumentDescriptionMap;
  149. #define ctkCommandLineParser_ctkInternal_declare_map(_NAME) \
  150. QHash<QString, CommandLineParser##_NAME##ArgumentDescription*> \
  151. LongArgTo##_NAME##ArgumentDescriptionMap; \
  152. QHash<QString, CommandLineParser##_NAME##ArgumentDescription*> \
  153. ShortArgTo##_NAME##ArgumentDescriptionMap;
  154. ctkCommandLineParser_ctkInternal_declare_map(String);
  155. ctkCommandLineParser_ctkInternal_declare_map(Boolean);
  156. ctkCommandLineParser_ctkInternal_declare_map(StringList);
  157. ctkCommandLineParser_ctkInternal_declare_map(Integer);
  158. #undef ctkCommandLineParser_ctkInternal_declare_map
  159. QStringList UnparsedArguments;
  160. QStringList ProcessedArguments;
  161. QString ErrorString;
  162. bool Debug;
  163. int FieldWidth;
  164. };
  165. // --------------------------------------------------------------------------
  166. // ctkCommandLineParser::ctkInternal methods
  167. // --------------------------------------------------------------------------
  168. CommandLineParserArgumentDescriptionBase*
  169. ctkCommandLineParser::ctkInternal::argumentDescription(const QString& argument)
  170. {
  171. if (this->ArgNameToArgumentDescriptionMap.contains(argument))
  172. {
  173. return this->ArgNameToArgumentDescriptionMap[argument];
  174. }
  175. return 0;
  176. }
  177. // --------------------------------------------------------------------------
  178. // ctkCommandLineParser methods
  179. // --------------------------------------------------------------------------
  180. ctkCommandLineParser::ctkCommandLineParser()
  181. {
  182. this->Internal = new ctkInternal();
  183. }
  184. // --------------------------------------------------------------------------
  185. ctkCommandLineParser::~ctkCommandLineParser()
  186. {
  187. delete this->Internal;
  188. }
  189. // --------------------------------------------------------------------------
  190. bool ctkCommandLineParser::parseArguments(const QStringList& arguments)
  191. {
  192. // Reset
  193. this->Internal->UnparsedArguments.clear();
  194. this->Internal->ProcessedArguments.clear();
  195. this->Internal->ErrorString.clear();
  196. bool ignoreRest = false;
  197. CommandLineParserArgumentDescriptionBase * currentArgDesc = 0;
  198. for(int i = 1; i < arguments.size(); ++i)
  199. {
  200. QString argument = arguments.at(i);
  201. if (this->Internal->Debug) { qDebug() << "Processing" << argument; }
  202. // should argument be ignored ?
  203. if (ignoreRest)
  204. {
  205. this->Internal->UnparsedArguments << argument;
  206. continue;
  207. }
  208. // Skip if argument has already been parsed ...
  209. if (this->Internal->ProcessedArguments.contains(argument))
  210. {
  211. qDebug() << "Skipping argument" << argument << " - Already processed !";
  212. continue;
  213. }
  214. // Retrieve corresponding argument description
  215. currentArgDesc = this->Internal->argumentDescription(argument);
  216. // Is there a corresponding argument description ?
  217. if (currentArgDesc)
  218. {
  219. this->Internal->ProcessedArguments << currentArgDesc->ShortArg << currentArgDesc->LongArg;
  220. int numberOfParametersToProcess = currentArgDesc->NumberOfParametersToProcess;
  221. ignoreRest = currentArgDesc->IgnoreRest;
  222. // Is the number of parameters associated with the argument being processed known ?
  223. if (numberOfParametersToProcess == 0)
  224. {
  225. currentArgDesc->addParameter("true");
  226. }
  227. else if (numberOfParametersToProcess > 0)
  228. {
  229. QString missingParameterError =
  230. "Argument %1 has %2 value(s) associated whereas exacly %3 are expected.";
  231. for(int j=1; j <= numberOfParametersToProcess; ++j)
  232. {
  233. if (i + j >= arguments.size())
  234. {
  235. this->Internal->ErrorString =
  236. missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess);
  237. if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
  238. return false;
  239. }
  240. QString parameter = arguments.at(i + j);
  241. if (this->Internal->Debug)
  242. {
  243. qDebug() << "Processing parameter" << j << ", value:" << parameter;
  244. }
  245. if (this->argumentAdded(parameter))
  246. {
  247. this->Internal->ErrorString =
  248. missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess);
  249. if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
  250. return false;
  251. }
  252. if (!currentArgDesc->addParameter(parameter))
  253. {
  254. this->Internal->ErrorString = QString(
  255. "Value(s) associated with argument %1 are incorrect. %2").
  256. arg(argument).arg(currentArgDesc->ExactMatchFailedMessage);
  257. if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
  258. return false;
  259. }
  260. }
  261. // Update main loop increment
  262. i = i + numberOfParametersToProcess;
  263. }
  264. else if (numberOfParametersToProcess == -1)
  265. {
  266. if (this->Internal->Debug)
  267. {
  268. qDebug() << "Proccessing StringList ...";
  269. }
  270. int j = 1;
  271. while(j + i < arguments.size())
  272. {
  273. if (this->argumentAdded(arguments.at(j + i)))
  274. {
  275. if (this->Internal->Debug)
  276. {
  277. qDebug() << "No more parameter for" << argument;
  278. }
  279. break;
  280. }
  281. QString parameter = arguments.at(j + i);
  282. if (this->Internal->Debug)
  283. {
  284. qDebug() << "Processing parameter" << j << ", value:" << parameter;
  285. }
  286. if (!currentArgDesc->addParameter(parameter))
  287. {
  288. this->Internal->ErrorString = QString(
  289. "Value(s) associated with argument %1 are incorrect. %2").
  290. arg(argument).arg(currentArgDesc->ExactMatchFailedMessage);
  291. if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
  292. return false;
  293. }
  294. j++;
  295. }
  296. // Update main loop increment
  297. i = i + j;
  298. }
  299. }
  300. else
  301. {
  302. this->Internal->UnparsedArguments << argument;
  303. }
  304. }
  305. return true;
  306. }
  307. // -------------------------------------------------------------------------
  308. QString ctkCommandLineParser::errorString()
  309. {
  310. return this->Internal->ErrorString;
  311. }
  312. // -------------------------------------------------------------------------
  313. const QStringList& ctkCommandLineParser::unparsedArguments()
  314. {
  315. return this->Internal->UnparsedArguments;
  316. }
  317. // -------------------------------------------------------------------------
  318. #define ctkCommandLineParser_addArgument_cxx_core(_NAME, _TYPE) \
  319. /* Make sure it's not already added */ \
  320. bool added = this->Internal->LongArgTo##_NAME##ArgumentDescriptionMap.contains(longarg); \
  321. Q_ASSERT(!added); \
  322. if (added) { return; } \
  323. \
  324. added = this->Internal->ShortArgTo##_NAME##ArgumentDescriptionMap.contains(shortarg); \
  325. Q_ASSERT(!added); \
  326. if (added) { return; } \
  327. \
  328. CommandLineParser##_NAME##ArgumentDescription * argDesc = \
  329. new CommandLineParser##_NAME##ArgumentDescription(longarg, shortarg, var, \
  330. argHelp, defaultValue, ignoreRest); \
  331. \
  332. Q_ASSERT(!(longarg == 0 && shortarg == 0)); \
  333. if (longarg == 0 && shortarg == 0) { return; } \
  334. if (longarg != 0) \
  335. { \
  336. this->Internal->LongArgTo##_NAME##ArgumentDescriptionMap[longarg] = argDesc; \
  337. int argWidth = QString(longarg).length() + 7; \
  338. if (argWidth > this->Internal->FieldWidth) \
  339. { \
  340. this->Internal->FieldWidth = argWidth; \
  341. } \
  342. } \
  343. if (shortarg != 0) \
  344. { \
  345. this->Internal->ShortArgTo##_NAME##ArgumentDescriptionMap[shortarg] = argDesc; \
  346. int argWidth = QString(shortarg).length() + 7; \
  347. if (argWidth > this->Internal->FieldWidth) \
  348. { \
  349. this->Internal->FieldWidth = argWidth; \
  350. } \
  351. } \
  352. this->Internal->ArgNameToArgumentDescriptionMap[longarg] = argDesc; \
  353. this->Internal->ArgNameToArgumentDescriptionMap[shortarg] = argDesc; \
  354. this->Internal->ArgumentDescriptionList << argDesc;
  355. // -------------------------------------------------------------------------
  356. #define ctkCommandLineParser_addArgument_cxx(_NAME, _TYPE) \
  357. void ctkCommandLineParser::add##_NAME##Argument(const char* longarg, \
  358. const char* shortarg, _TYPE* var, const QString& argHelp, const _TYPE& defaultValue, \
  359. bool ignoreRest) \
  360. { \
  361. ctkCommandLineParser_addArgument_cxx_core(_NAME, _TYPE); \
  362. }
  363. // -------------------------------------------------------------------------
  364. #define ctkCommandLineParser_addArgument_cxx_without_ignore_rest(_NAME, _TYPE) \
  365. void ctkCommandLineParser::add##_NAME##Argument(const char* longarg, \
  366. const char* shortarg, _TYPE* var, const QString& argHelp, const _TYPE& defaultValue) \
  367. { \
  368. bool ignoreRest = false; \
  369. ctkCommandLineParser_addArgument_cxx_core(_NAME, _TYPE); \
  370. }
  371. // --------------------------------------------------------------------------
  372. ctkCommandLineParser_addArgument_cxx(String, QString);
  373. ctkCommandLineParser_addArgument_cxx(Boolean, bool);
  374. ctkCommandLineParser_addArgument_cxx_without_ignore_rest(StringList, QStringList);
  375. ctkCommandLineParser_addArgument_cxx(Integer, int);
  376. #undef ctkCommandLineParser_addArgument_cxx
  377. // --------------------------------------------------------------------------
  378. bool ctkCommandLineParser::setExactMatchRegularExpression(
  379. const QString& argument, const QString& expression, const QString& exactMatchFailedMessage)
  380. {
  381. CommandLineParserArgumentDescriptionBase * argDesc =
  382. this->Internal->argumentDescription(argument);
  383. if (!argDesc)
  384. {
  385. return false;
  386. }
  387. if (argDesc->ArgumentType == "bool")
  388. {
  389. return false;
  390. }
  391. argDesc->RegularExpression = expression;
  392. argDesc->ExactMatchFailedMessage = exactMatchFailedMessage;
  393. return true;
  394. }
  395. // --------------------------------------------------------------------------
  396. QString ctkCommandLineParser::helpText(const char charPad)
  397. {
  398. QString text;
  399. QTextStream stream(&text);
  400. // Loop over argument descriptions
  401. foreach(CommandLineParserArgumentDescriptionBase* argDesc,
  402. this->Internal->ArgumentDescriptionList)
  403. {
  404. stream << argDesc->helpText(this->Internal->FieldWidth, charPad);
  405. }
  406. return text;
  407. }
  408. // --------------------------------------------------------------------------
  409. bool ctkCommandLineParser::argumentAdded(const QString& argument)
  410. {
  411. return this->Internal->ArgNameToArgumentDescriptionMap.contains(argument);
  412. }
  413. // --------------------------------------------------------------------------
  414. bool ctkCommandLineParser::argumentParsed(const QString& argument)
  415. {
  416. return this->Internal->ProcessedArguments.contains(argument);
  417. }