ctkSlicerModuleReader.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. /*=============================================================================
  2. Library: CTK
  3. Copyright (c) 2010 Brigham and Women's Hospital (BWH) All Rights Reserved.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =============================================================================*/
  14. // Qt includes
  15. #include <QAbstractMessageHandler>
  16. #include <QDebug>
  17. #include <QXmlSchema>
  18. #include <QXmlSchemaValidator>
  19. #include <QVariant>
  20. // CTK includes
  21. #include "ctkSlicerModuleReader.h"
  22. // STD includes
  23. #include <stdexcept>
  24. class ctkDefaultMessageHandler : public QAbstractMessageHandler
  25. {
  26. public:
  27. ctkDefaultMessageHandler(): QAbstractMessageHandler(0)
  28. {
  29. }
  30. QString statusMessage() const
  31. {
  32. return m_description;
  33. }
  34. int line() const
  35. {
  36. return m_sourceLocation.line();
  37. }
  38. int column() const
  39. {
  40. return m_sourceLocation.column();
  41. }
  42. protected:
  43. virtual void handleMessage(QtMsgType type, const QString &description,
  44. const QUrl &identifier, const QSourceLocation &sourceLocation)
  45. {
  46. Q_UNUSED(type);
  47. Q_UNUSED(identifier);
  48. m_messageType = type;
  49. m_description = description;
  50. m_sourceLocation = sourceLocation;
  51. }
  52. private:
  53. QtMsgType m_messageType;
  54. QString m_description;
  55. QSourceLocation m_sourceLocation;
  56. };
  57. // ----------------------------------------------------------------------------
  58. bool ctkSlicerModuleReader::validate()const
  59. {
  60. ctkDefaultMessageHandler errorHandler;
  61. QXmlSchema schema;
  62. schema.setMessageHandler(&errorHandler);
  63. schema.load(QUrl::fromLocalFile(":slicerModuleDescription.xsd"));
  64. bool res = schema.isValid();
  65. if (!res)
  66. {
  67. QString error = errorHandler.statusMessage();
  68. throw std::runtime_error( tr("Invalid Schema %1")
  69. .arg(errorHandler.statusMessage()).toStdString() );
  70. }
  71. QXmlSchemaValidator validator(schema);
  72. res = validator.validate(this->Device);
  73. if (!res)
  74. {
  75. throw std::runtime_error( tr("Invalid XML(%1,%2):\n %3")
  76. .arg(errorHandler.line())
  77. .arg(errorHandler.column())
  78. .arg(errorHandler.statusMessage()).toStdString());
  79. }
  80. this->Device->reset();
  81. return res;
  82. }
  83. // ----------------------------------------------------------------------------
  84. void ctkSlicerModuleReader::update()
  85. {
  86. // Verify the xml is correct
  87. this->validate();
  88. QXmlSimpleReader xmlReader;
  89. QXmlInputSource *source = new QXmlInputSource(this->Device);
  90. ctkSlicerModuleHandler handler;
  91. handler.setModuleDescription(&this->ModuleDescription);
  92. xmlReader.setContentHandler(&handler);
  93. xmlReader.setErrorHandler(&handler);
  94. bool res = xmlReader.parse(source);
  95. if (!res)
  96. {
  97. throw std::runtime_error( tr("Parse error %1")
  98. .arg(handler.errorString()).toStdString() );
  99. }
  100. }
  101. // ----------------------------------------------------------------------------
  102. ctkSlicerModuleHandler::ctkSlicerModuleHandler()
  103. {
  104. this->ModuleDescription = 0;
  105. this->State.CurrentParameter = 0;
  106. this->State.CurrentGroup = 0;
  107. this->State.InExecutable = 0;
  108. this->State.InGroup = 0;
  109. this->State.InParameter = 0;
  110. this->ParamValidator = QRegExp("\\W");
  111. }
  112. // ----------------------------------------------------------------------------
  113. void ctkSlicerModuleHandler::setModuleDescription(ctkModuleDescription* moduleDescription)
  114. {
  115. this->ModuleDescription = moduleDescription;
  116. }
  117. // ----------------------------------------------------------------------------
  118. ctkModuleDescription* ctkSlicerModuleHandler::moduleDescription()const
  119. {
  120. return this->ModuleDescription;
  121. }
  122. // ----------------------------------------------------------------------------
  123. bool ctkSlicerModuleHandler::characters(const QString& ch)
  124. {
  125. this->State.CurrentText = ch.trimmed();
  126. return true;
  127. }
  128. // ----------------------------------------------------------------------------
  129. bool ctkSlicerModuleHandler::startElement(const QString& namespaceURI, const QString& localName,
  130. const QString& name, const QXmlAttributes& atts)
  131. {
  132. if (this->State.CurrentGroup == 0)
  133. {
  134. return this->startExecutableElement(namespaceURI, localName, name, atts);
  135. }
  136. else if (this->State.CurrentParameter == 0)
  137. {
  138. return this->startGroupElement(namespaceURI, localName, name, atts);
  139. }
  140. else
  141. {
  142. return this->startParameterElement(namespaceURI, localName, name, atts);
  143. }
  144. return false;
  145. }
  146. // ----------------------------------------------------------------------------
  147. bool ctkSlicerModuleHandler::endElement(const QString& namespaceURI,
  148. const QString& localName,
  149. const QString& qName)
  150. {
  151. if (this->State.InParameter)
  152. {
  153. return this->endParameterElement(namespaceURI, localName, qName);
  154. }
  155. else if (this->State.InGroup)
  156. {
  157. return this->endGroupElement(namespaceURI, localName, qName);
  158. }
  159. else if (this->State.InExecutable)
  160. {
  161. return this->endExecutableElement(namespaceURI, localName, qName);
  162. }
  163. return false;
  164. }
  165. // ----------------------------------------------------------------------------
  166. bool ctkSlicerModuleHandler::startExecutableElement(const QString& namespaceURI,
  167. const QString& localName,
  168. const QString& name,
  169. const QXmlAttributes& atts)
  170. {
  171. Q_UNUSED(namespaceURI);
  172. Q_UNUSED(localName);
  173. ++this->State.InExecutable;
  174. ctkModuleParameterGroup group;
  175. if (name == "parameters")
  176. {
  177. if (!atts.value("advanced").isEmpty())
  178. {
  179. group["Advanced"] = QVariant(atts.value("advanced")).toBool() ? "true" : "false";
  180. }
  181. this->State.CurrentGroup = new ctkModuleParameterGroup(group);
  182. }
  183. return true;
  184. }
  185. // ----------------------------------------------------------------------------
  186. bool ctkSlicerModuleHandler::startGroupElement(const QString& namespaceURI,
  187. const QString& localName,
  188. const QString& name,
  189. const QXmlAttributes& atts)
  190. {
  191. Q_UNUSED(namespaceURI);
  192. Q_UNUSED(localName);
  193. ++this->State.InGroup;
  194. if (name == "label" || name == "description")
  195. {// not a parameter
  196. return true;
  197. }
  198. ctkModuleParameter param;
  199. param["Tag"] = name;
  200. bool multiple = QVariant(atts.value("multiple")).toBool(); //empty (not found) is false
  201. bool hidden = QVariant(atts.value("hidden")).toBool(); //empty (not found) is false
  202. if (name == "integer" || name == "integer-vector")
  203. {
  204. if (name == "integer-vector")
  205. {
  206. multiple = true;
  207. }
  208. param["Multiple"] = multiple ? "true" : "false";
  209. param["CPPStyle"] = multiple ? "std::vector<int>" : "int";
  210. param["ArgType"] = "int";
  211. param["StringToType"] = "atoi";
  212. }
  213. else if (name == "float" || name == "float-vector")
  214. {
  215. if (name == "integer-vector")
  216. {
  217. multiple = true;
  218. }
  219. param["Multiple"] = multiple ? "true" : "false";
  220. param["CPPStyle"] = multiple ? "std::vector<float>" : "float";
  221. param["ArgType"] = "float";
  222. param["StringToType"] = "atof";
  223. }
  224. else if (name == "double" || name == "double-vector")
  225. {
  226. if (name == "double-vector")
  227. {
  228. multiple = true;
  229. }
  230. param["Multiple"] = multiple ? "true" : "false";
  231. param["CPPStyle"] = multiple ? "std::vector<double>" : "double";
  232. param["ArgType"] = "double";
  233. param["StringToType"] = "atof";
  234. }
  235. else if (name == "string")
  236. {
  237. if (name == "string-vector")
  238. {
  239. multiple = true;
  240. }
  241. param["Multiple"] = multiple ? "true" : "false";
  242. param["CPPStyle"] = multiple ? "std::vector<std::string>" : "std::string";
  243. param["ArgType"] = "std::string";
  244. param["StringToType"] = "";
  245. }
  246. else if (name == "boolean")
  247. {
  248. if (name == "boolean-vector")
  249. {
  250. multiple = true;
  251. }
  252. param["Multiple"] = multiple ? "true" : "false";
  253. param["CPPStyle"] = multiple ? "std::vector<bool>" : "bool";
  254. param["ArgType"] = "bool";
  255. param["StringToType"] = "";
  256. param["Hidden"] = hidden ? "true" : "false";
  257. }
  258. else if (name == "point" || name == "point-vector" ||
  259. name == "region" || name == "region-vector")
  260. {
  261. if (name == "point-vector" || name == "region-vector")
  262. {
  263. multiple = true;
  264. }
  265. param["Multiple"] = multiple ? "true" : "false";
  266. param["CPPStyle"] = multiple ? "std::vector<std::vector<float> >" : "std::vector<float>";
  267. param["ArgType"] = "float";
  268. param["StringToType"] = "atof";
  269. if (!atts.value("coordinateSystem").isEmpty())
  270. {
  271. param["CoordinateSystem"] = atts.value("coordinateSystem");
  272. }
  273. }
  274. else if (name == "string-enumeration")
  275. {
  276. param["CPPStyle"] = "std::string";
  277. }
  278. else if (name == "integer-enumeration")
  279. {
  280. param["CPPStyle"] = "int";
  281. }
  282. else if (name == "float-enumeration")
  283. {
  284. param["CPPStyle"] = "float";
  285. }
  286. else if (name == "double-enumeration")
  287. {
  288. param["CPPStyle"] = "double";
  289. }
  290. else if (name == "file")
  291. {
  292. param["Multiple"] = multiple ? "true" : "false";
  293. param["CPPType"] = multiple ? "std::vector<std::string>" : "std::string";
  294. param["ArgType"] = "std::string";
  295. param["Type"] = "scalar";
  296. if (!atts.value("fileExtensions").isEmpty())
  297. {
  298. param["FileExtensionsAsString"] = atts.value("fileExtensions");
  299. }
  300. }
  301. else if (name == "directory")
  302. {
  303. param["Multiple"] = multiple ? "true" : "false";
  304. param["CPPStyle"] = multiple ? "std::vector<std::string>" : "std::string";
  305. param["ArgType"] = "std::string";
  306. }
  307. else if (name == "transform")
  308. {
  309. param["Multiple"] = multiple ? "true" : "false";
  310. param["CPPStyle"] = multiple ? "std::vector<std::string>" : "std::string";
  311. param["ArgType"] = "std::string";
  312. param["Type"] = atts.value("type").isEmpty() ? atts.value("type") : "unknown";
  313. if (!atts.value("fileExtensions").isEmpty())
  314. {
  315. param["FileExtensionsAsString"] = atts.value("fileExtensions");
  316. }
  317. if (!atts.value("reference").isEmpty())
  318. {
  319. param["Reference"] = atts.value("reference");
  320. }
  321. }
  322. else if (name == "image")
  323. {
  324. param["Multiple"] = multiple ? "true" : "false";
  325. param["CPPStyle"] = multiple ? "std::vector<std::string>" : "std::string";
  326. param["ArgType"] = "std::string";
  327. param["Type"] = (!atts.value("type").isEmpty()) ? atts.value("type") : "scalar";
  328. if (!atts.value("fileExtensions").isEmpty())
  329. {
  330. param["FileExtensionsAsString"] = atts.value("fileExtensions");
  331. }
  332. param["Hidden"] = hidden ? "true" : "false";
  333. if (!atts.value("reference").isEmpty())
  334. {
  335. param["Reference"] = atts.value("reference");
  336. }
  337. }
  338. else if (name == "geometry")
  339. {
  340. bool aggregate = QVariant(atts.value("aggregate")).toBool();
  341. param["Multiple"] = multiple ? "true" : "false";
  342. param["Aggregate"] = aggregate ? "true" : "false";
  343. param["CPPType"] = (multiple && !aggregate) ? "std::vector<std::string>" : "std::string";
  344. param["ArgType"] = "std::string";
  345. param["Type"] = (!atts.value("type").isEmpty())? atts.value("type") : "scalar";
  346. }
  347. else if (name == "table")
  348. {
  349. param["Multiple"] = multiple ? "true" : "false";
  350. param["CPPType"] = multiple ? "std::vector<std::string>" : "std::string";
  351. param["ArgType"] = "std::string";
  352. param["Type"] = (!atts.value("type").isEmpty())? atts.value("type") : "scalar";
  353. if (!atts.value("reference").isEmpty())
  354. {
  355. param["Reference"] = atts.value("reference");
  356. }
  357. if (!atts.value("fileExtensions").isEmpty())
  358. {
  359. param["FileExtensionsAsString"] = atts.value("fileExtensions");
  360. }
  361. }
  362. else if (name == "measurement")
  363. {
  364. param["Multiple"] = multiple ? "true" : "false";
  365. param["CPPType"] = multiple ? "std::vector<std::string>" : "std::string";
  366. param["ArgType"] = "std::string";
  367. param["Type"] = (!atts.value("type").isEmpty())? atts.value("type") : "scalar";
  368. param["Hidden"] = hidden ? "true" : "false";
  369. if (!atts.value("reference").isEmpty())
  370. {
  371. param["Reference"] = atts.value("reference");
  372. }
  373. if (!atts.value("fileExtensions").isEmpty())
  374. {
  375. param["FileExtensionsAsString"] = atts.value("fileExtensions");
  376. }
  377. }
  378. this->State.CurrentParameter = new ctkModuleParameter(param);
  379. return true;
  380. }
  381. // ----------------------------------------------------------------------------
  382. bool ctkSlicerModuleHandler::startParameterElement(const QString& namespaceURI, const QString& localName,
  383. const QString& name, const QXmlAttributes& atts)
  384. {
  385. Q_UNUSED(namespaceURI);
  386. Q_UNUSED(localName);
  387. ++this->State.InParameter;
  388. ctkModuleParameter& param = *this->State.CurrentParameter;
  389. if (name == "flag")
  390. {
  391. if (!atts.value("alias").isEmpty())
  392. {
  393. param["FlagAliasesAsString"] = atts.value("alias");
  394. }
  395. if (!atts.value("deprecatedalias").isEmpty())
  396. {
  397. param["DeprecatedFlagAliasesAsString"] = atts.value("deprecatedalias");
  398. }
  399. }
  400. else if (name == "longflag")
  401. {
  402. if (!atts.value("alias").isEmpty())
  403. {
  404. param["LongFlagAliasesAsString"] = atts.value("alias");
  405. }
  406. if (!atts.value("deprecatedalias").isEmpty())
  407. {
  408. param["DeprecatedLongFlagAliasesAsString"] = atts.value("deprecatedalias");
  409. }
  410. }
  411. else if (name == "constraints")
  412. {
  413. param["Constraints"] = "true";
  414. }
  415. return true;
  416. }
  417. // ----------------------------------------------------------------------------
  418. bool ctkSlicerModuleHandler::endExecutableElement(const QString& namespaceURI,
  419. const QString& localName,
  420. const QString& name)
  421. {
  422. Q_UNUSED(namespaceURI);
  423. Q_UNUSED(localName);
  424. --this->State.InExecutable;
  425. ctkModuleDescription& module= *this->ModuleDescription;
  426. if (name == "parameters")
  427. {
  428. Q_ASSERT(this->State.CurrentGroup);
  429. this->ModuleDescription->addParameterGroup(this->State.CurrentGroup);
  430. this->State.CurrentGroup = 0;
  431. }
  432. else if (name == "category")
  433. {
  434. module["Category"] = this->State.CurrentText;
  435. }
  436. else if (name == "index")
  437. {
  438. module["Index"] = this->State.CurrentText;
  439. }
  440. else if (name == "title")
  441. {
  442. module["Title"] = this->State.CurrentText;
  443. }
  444. else if (name == "version")
  445. {
  446. module["Version"] = this->State.CurrentText;
  447. }
  448. else if (name == "documentation-url")
  449. {
  450. module["DocumentationUrl"] = this->State.CurrentText;
  451. }
  452. else if (name == "license")
  453. {
  454. module["License"] = this->State.CurrentText;
  455. }
  456. else if (name == "acknowledgements")
  457. {
  458. module["License"] = this->State.CurrentText;
  459. }
  460. else if (name == "contributor")
  461. {
  462. module["Contributor"] = this->State.CurrentText;
  463. }
  464. else if (name == "location")
  465. {
  466. module["Location"] = this->State.CurrentText;
  467. }
  468. else if (name == "description")
  469. {
  470. module["Description"] = this->State.CurrentText.replace('\"','\'');
  471. }
  472. return true;
  473. }
  474. // ----------------------------------------------------------------------------
  475. bool ctkSlicerModuleHandler::endGroupElement(const QString& namespaceURI,
  476. const QString& localName,
  477. const QString& name)
  478. {
  479. Q_UNUSED(namespaceURI);
  480. Q_UNUSED(localName);
  481. --this->State.InGroup;
  482. Q_ASSERT(this->State.CurrentGroup);
  483. ctkModuleParameterGroup& group = *this->State.CurrentGroup;
  484. if (name == "label")
  485. {
  486. group["Label"] = this->State.CurrentText;
  487. return true;
  488. }
  489. else if (name == "description")
  490. {
  491. group["Description"] = this->State.CurrentText.replace('\"', '\'');
  492. return true;
  493. }
  494. Q_ASSERT(this->State.CurrentParameter);
  495. this->State.CurrentGroup->addParameter(this->State.CurrentParameter);
  496. this->State.CurrentParameter = 0;
  497. return true;
  498. }
  499. // ----------------------------------------------------------------------------
  500. bool ctkSlicerModuleHandler::endParameterElement(const QString& namespaceURI, const QString& localName,
  501. const QString& name)
  502. {
  503. Q_UNUSED(namespaceURI);
  504. Q_UNUSED(localName);
  505. --this->State.InParameter;
  506. bool res = true;
  507. ctkModuleParameter& parameter = *this->State.CurrentParameter;
  508. if (name == "flag")
  509. {
  510. QString flag = this->State.CurrentText;
  511. flag.remove(0, flag.indexOf('-') + 1);
  512. res = (flag.size() == 1);
  513. if (!res)
  514. {
  515. qWarning() << "Invalid flag" << flag;
  516. }
  517. parameter["Flag"] = flag;
  518. }
  519. else if (name == "longflag")
  520. {
  521. QString flag = this->State.CurrentText;
  522. flag.remove(0, flag.indexOf("--") + 2);
  523. res = (flag.size() != 0 && this->ParamValidator.indexIn(flag) == -1);
  524. if (!res)
  525. {
  526. qWarning() << "Invalid flag" << flag;
  527. }
  528. parameter["LongFlag"] = flag;
  529. parameter["Name"] = parameter.value("Name", flag);
  530. }
  531. else if (name == "name")
  532. {
  533. QString paramName = this->State.CurrentText;
  534. if (this->ParamValidator.indexIn(paramName) != -1)
  535. {
  536. res = false;
  537. qWarning() << "Invalid name" << paramName;
  538. }
  539. parameter["Name"] = paramName;
  540. }
  541. else if (name == "label")
  542. {
  543. parameter["Label"] = this->State.CurrentText;
  544. }
  545. else if (name == "element")
  546. {
  547. parameter.insertMulti("Element", this->State.CurrentText);
  548. }
  549. else if (name == "default")
  550. {
  551. parameter["Default"] = this->State.CurrentText;
  552. }
  553. else if (name == "channel")
  554. {
  555. parameter["Channel"] = this->State.CurrentText;
  556. }
  557. else if (name == "index")
  558. {
  559. // TODO make sure Flag and LongFlag are empty
  560. parameter["Index"] = this->State.CurrentText;
  561. }
  562. else if (name == "minimum")
  563. {
  564. parameter["Minimum"] = this->State.CurrentText;
  565. }
  566. else if (name == "maximum")
  567. {
  568. parameter["Maximum"] = this->State.CurrentText;
  569. }
  570. else if (name == "step")
  571. {
  572. parameter["Step"] = this->State.CurrentText;
  573. }
  574. return res;
  575. }