ctkCommandLineParser.cpp 19 KB

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