Pārlūkot izejas kodu

Add Slicer Command line module XML Schema

Automatically validate the Slicer module descriptions before parsing them.
Julien Finet 14 gadi atpakaļ
vecāks
revīzija
3c5ccf8e08

+ 2 - 0
Plugins/org.commontk.slicermodule/CMakeLists.txt

@@ -24,6 +24,7 @@ SET(PLUGIN_UI_FORMS
 
 # QRC Files which should be compiled into the plugin
 SET(PLUGIN_resources
+  Resources/org_commontk_slicermodule.qrc
 )
 
 
@@ -46,6 +47,7 @@ ctkMacroBuildPlugin(
 )
 
 SET( QT_USE_QTXML ON )
+SET( QT_USE_QTXMLPATTERNS ON )
 INCLUDE(${QT_USE_FILE})
 TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${QT_LIBRARIES})
 

+ 144 - 0
Plugins/org.commontk.slicermodule/Resources/Xml/slicerModuleDescription.xsd

@@ -0,0 +1,144 @@
+<?xml version="1.0"?>
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+    <xsd:element name="executable">
+        <xsd:complexType>
+            <xsd:sequence>
+                <xsd:element name="category" type="xsd:string"/>
+                <xsd:element name="title" type="xsd:string"/>
+                <xsd:element name="description" type="xsd:string"/>
+                <xsd:element name="version" type="xsd:string"/>
+                <xsd:element name="documentation-url" type="xsd:string"/>
+                <xsd:element name="license" type="xsd:string"/>
+                <xsd:element name="contributor" type="xsd:string"/>
+                <xsd:element name="parameters" type="parameters" maxOccurs="unbounded"/>
+            </xsd:sequence>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:complexType name="parameters">
+      <xsd:choice minOccurs="0" maxOccurs="unbounded">
+        <xsd:element name="label" type="xsd:string"/>
+        <xsd:element name="description" type="xsd:string"/>
+        <xsd:element name="integer" type="scalarType"/>
+        <xsd:element name="integer-vector" type="scalarType"/>
+        <xsd:element name="boolean" type="scalarType"/>
+        <xsd:element name="boolean-vector" type="scalarType"/>
+        <xsd:element name="float" type="scalarType"/>
+        <xsd:element name="float-vector" type="scalarType"/>
+        <xsd:element name="double" type="scalarType"/>
+        <xsd:element name="double-vector" type="scalarType"/>
+        <xsd:element name="string" type="paramType"/>
+        <xsd:element name="string-vector" type="paramType"/>
+        <xsd:element name="point" type="pointType"/>
+        <xsd:element name="point-vector" type="pointType"/>
+        <xsd:element name="region" type="pointType"/>
+        <xsd:element name="region-vector" type="pointType"/>
+        <xsd:element name="string-enumeration" type="enumerationType"/>
+        <xsd:element name="integer-enumeration" type="enumerationType"/>
+        <xsd:element name="float-enumeration" type="enumerationType"/>
+        <xsd:element name="double-enumeration" type="enumerationType"/>
+        <xsd:element name="file" type="indexedParamType"/>
+        <xsd:element name="directory" type="paramType"/>
+        <xsd:element name="transform" type="typedIndexParamType"/>
+        <xsd:element name="image" type="typedIndexParamType"/>
+        <xsd:element name="geometry" type="typedIndexParamType"/>
+        <xsd:element name="table" type="typedIndexParamType"/>
+        <xsd:element name="measurement" type="typedIndexParamType"/>
+      </xsd:choice>
+    </xsd:complexType>
+
+    <xsd:complexType name="paramType">    
+      <xsd:choice minOccurs="0" maxOccurs="unbounded">
+        <xsd:element name="name" type="xsd:string"/>
+        <xsd:element name="flag" type="flagType" />
+        <xsd:element name="longflag" type="flagType"/>
+        <xsd:element name="description" type="xsd:string"/>
+        <xsd:element name="label" type="xsd:string"/>
+        <xsd:element name="default" type="xsd:string"/>
+      </xsd:choice>
+      <xsd:attribute name="multiple" type="xsd:boolean"/>
+      <xsd:attribute name="hidden" type="xsd:boolean"/>
+    </xsd:complexType>
+    
+    <xsd:complexType name="scalarType" >
+      <xsd:complexContent>
+        <xsd:extension base="paramType">
+          <xsd:sequence>
+            <xsd:element name="constraints" type="constraintsType"  minOccurs="0" />
+          </xsd:sequence>
+        </xsd:extension>
+      </xsd:complexContent>
+    </xsd:complexType>
+
+    <xsd:complexType name="pointType" >
+      <xsd:complexContent>
+        <xsd:extension base="paramType">
+          <xsd:attribute name="coordinateSystem">
+            <xsd:simpleType>
+              <xsd:restriction base="xsd:string">
+                <xsd:enumeration value="RAS"/>
+                <xsd:enumeration value="IJK"/>
+              </xsd:restriction>
+            </xsd:simpleType>
+          </xsd:attribute>
+        </xsd:extension>
+      </xsd:complexContent>
+    </xsd:complexType>
+
+    <xsd:complexType name="enumerationType" >
+      <xsd:complexContent>
+        <xsd:extension base="paramType">
+          <xsd:sequence>
+            <xsd:element name="element" type="xsd:string"/>
+          </xsd:sequence>
+        </xsd:extension>
+      </xsd:complexContent>
+    </xsd:complexType>
+
+    <xsd:complexType name="indexedParamType" >
+      <xsd:all>
+        <xsd:element name="name" type="xsd:string"/>
+        <xsd:element name="label" type="xsd:string"/>
+        <xsd:element name="description" type="xsd:string"/>
+        <xsd:element name="channel" maxOccurs="1">
+          <xsd:simpleType>
+            <xsd:restriction base="xsd:string">
+              <xsd:enumeration value="input"/>
+              <xsd:enumeration value="output"/>
+            </xsd:restriction>
+          </xsd:simpleType>
+        </xsd:element>
+        <xsd:element name="index" type="xsd:nonNegativeInteger"/>
+      </xsd:all>
+      <xsd:attribute name="fileExtensions" type="xsd:string"/>
+    </xsd:complexType>
+
+    <xsd:complexType name="typedIndexParamType" >
+      <xsd:complexContent>
+        <xsd:extension base="indexedParamType">
+          <xsd:attribute name="reference" type="xsd:string"/>
+          <xsd:attribute name="type" type="xsd:string"/>
+          <xsd:attribute name="hidden" type="xsd:boolean"/>
+        </xsd:extension>
+      </xsd:complexContent>
+    </xsd:complexType>
+    
+    <xsd:complexType name="flagType">
+      <xsd:simpleContent>
+        <xsd:extension base="xsd:string">
+          <xsd:attribute name="alias" type="xsd:string"/>
+          <xsd:attribute name="deprecatedalias" type="xsd:string"/>
+        </xsd:extension>
+      </xsd:simpleContent>
+    </xsd:complexType>
+    
+    <xsd:complexType name="constraintsType">
+      <xsd:sequence>
+        <xsd:element name="minimum" type="xsd:double"/>
+        <xsd:element name="maximum" type="xsd:double"/>
+        <xsd:element name="step" type="xsd:double"/>
+      </xsd:sequence>
+    </xsd:complexType>
+
+</xsd:schema>

+ 5 - 0
Plugins/org.commontk.slicermodule/Resources/org_commontk_slicermodule.qrc

@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+  <qresource>
+    <file alias="slicerModuleDescription.xsd">Xml/slicerModuleDescription.xsd</file>
+  </qresource>
+</RCC>

+ 2 - 1
Plugins/org.commontk.slicermodule/Testing/Cpp/ctkSlicerModuleTest.cpp

@@ -19,6 +19,7 @@
 =========================================================================*/
 
 // Qt includes
+#include <QApplication>
 #include <QFile>
 
 // CTK includes
@@ -40,7 +41,7 @@ void BuildCommandLine( ctkPluginContext* context, const ctkModuleDescription& mo
 //-----------------------------------------------------------------------------
 int ctkSlicerModuleTest(int argc, char * argv [] )
 {
-
+  QApplication app(argc, argv);
   ctkCommandLineParser parser;
   parser.addArgument("", "-F", QVariant::String);
   bool ok = false;

+ 72 - 0
Plugins/org.commontk.slicermodule/ctkSlicerModuleReader.cpp

@@ -24,10 +24,82 @@ limitations under the License.
 
 // CTK includes
 #include "ctkSlicerModuleReader.h"
+class ctkDefaultMessageHandler : public QAbstractMessageHandler
+{
+public:
+  ctkDefaultMessageHandler(): QAbstractMessageHandler(0)
+  {
+  }
+
+  QString statusMessage() const
+  {
+  return m_description;
+  }
+
+  int line() const
+  {
+    return m_sourceLocation.line();
+  }
+
+  int column() const
+  {
+    return m_sourceLocation.column();
+  }
+
+protected:
+  virtual void handleMessage(QtMsgType type, const QString &description,
+                             const QUrl &identifier, const QSourceLocation &sourceLocation)
+  {
+    Q_UNUSED(type);
+    Q_UNUSED(identifier);
+
+    m_messageType = type;
+    m_description = description;
+    m_sourceLocation = sourceLocation;
+  }
+
+private:
+   QtMsgType m_messageType;
+   QString m_description;
+   QSourceLocation m_sourceLocation;
+ };
+ 
+// ----------------------------------------------------------------------------
+bool ctkSlicerModuleReader::validate()const
+{
+  ctkDefaultMessageHandler errorHandler;
+
+  QXmlSchema schema;
+  schema.setMessageHandler(&errorHandler);
+  schema.load(QUrl::fromLocalFile(":slicerModuleDescription.xsd"));
+  
+  bool res = schema.isValid();
+  if (!res)
+    {
+    QString error = errorHandler.statusMessage();
+    throw std::runtime_error( tr("Invalid Schema %1")
+      .arg(errorHandler.statusMessage()).toStdString() );
+    }
+
+  QXmlSchemaValidator validator(schema);
+  res = validator.validate(this->Device);
+
+  if (!res)
+    {
+    throw std::runtime_error( tr("Invalid XML(%1,%2):\n %3")
+      .arg(errorHandler.line())
+      .arg(errorHandler.column())
+      .arg(errorHandler.statusMessage()).toStdString());
+    }
+  this->Device->reset();
+  return res;
+}
 
 // ----------------------------------------------------------------------------
 void ctkSlicerModuleReader::update()
 {
+  // Verify the xml is correct
+  this->validate();
 
   QXmlSimpleReader xmlReader;
   QXmlInputSource *source = new QXmlInputSource(this->Device);

+ 1 - 0
Plugins/org.commontk.slicermodule/ctkSlicerModuleReader.h

@@ -40,6 +40,7 @@ class ctkSlicerModuleReader : public ctkModuleDescriptionReader
   Q_OBJECT
 public:
   virtual void update();
+  bool validate()const; 
 };
 
 /**