Ver código fonte

Added alternative way for "parsing" a CLI XML description.

Instead of parsing it, we transform it using a XML stylesheet. The
result is a valid Qt UI file which can be dynamically loaded at
runtime. Support classes take care of validating and transforming
the files.
Sascha Zelzer 13 anos atrás
pai
commit
622fca769f

+ 6 - 0
Libs/ModuleDescription/CMakeLists.txt

@@ -17,6 +17,12 @@ set(KIT_SRCS
   ctkModuleDescription.h
   ctkModuleDescription.cpp
   ctkModuleDescriptionParser.cpp
+  ctkModuleDescriptionValidator.h
+  ctkModuleDescriptionValidator.cpp
+  ctkModuleManager.h
+  ctkModuleManager.cpp
+  ctkModuleObjectHierarchyReader.h
+  ctkModuleObjectHierarchyReader.cpp
   ctkModuleParameter.h
   ctkModuleParameter.cpp
   ctkModuleParameterGroup.h

+ 259 - 0
Libs/ModuleDescription/ctkModuleDescriptionValidator.cpp

@@ -0,0 +1,259 @@
+/*=============================================================================
+  
+  Library: CTK
+  
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+    
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  
+=============================================================================*/
+
+#include "ctkModuleDescriptionValidator.h"
+
+#include <QFile>
+#include <QBuffer>
+#include <QXmlStreamReader>
+#include <QXmlSchema>
+#include <QXmlSchemaValidator>
+#include <QXmlQuery>
+#include <QAbstractMessageHandler>
+
+#include <QDebug>
+
+class _MessageHandler : public QAbstractMessageHandler
+{
+public:
+
+  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(identifier)
+
+    m_messageType = type;
+    m_sourceLocation = sourceLocation;
+
+    QXmlStreamReader reader(description);
+    m_description.clear();
+    m_description.reserve(description.size());
+    while(!reader.atEnd())
+    {
+      reader.readNext();
+
+      switch(reader.tokenType())
+      {
+      case QXmlStreamReader::Characters:
+      {
+        m_description.append(reader.text().toString());
+        continue;
+      }
+      case QXmlStreamReader::StartElement:
+        /* Fallthrough, */
+      case QXmlStreamReader::EndElement:
+        /* Fallthrough, */
+      case QXmlStreamReader::StartDocument:
+        /* Fallthrough, */
+      case QXmlStreamReader::EndDocument:
+        continue;
+      default:
+        Q_ASSERT_X(false, Q_FUNC_INFO,
+                   "Unexpected node.");
+      }
+    }
+  }
+
+private:
+  QtMsgType m_messageType;
+  QString m_description;
+  QSourceLocation m_sourceLocation;
+};
+
+ctkModuleDescriptionValidator::ctkModuleDescriptionValidator(QIODevice *input)
+  : _input(input), _inputSchema(0), _outputSchema(0), _transformation(0)
+{
+}
+
+void ctkModuleDescriptionValidator::setInput(QIODevice *input)
+{
+  _input = input;
+}
+
+QString ctkModuleDescriptionValidator::output()
+{
+  return _output;
+}
+
+void ctkModuleDescriptionValidator::setInputSchema(QIODevice *input)
+{
+  _inputSchema = input;
+}
+
+void ctkModuleDescriptionValidator::setOutputSchema(QIODevice *output)
+{
+  _outputSchema = output;
+}
+
+void ctkModuleDescriptionValidator::setXSLTransformation(QIODevice *transformation)
+{
+  _transformation = transformation;
+}
+
+bool ctkModuleDescriptionValidator::validate()
+{
+  return validateInput() && validateOutput();
+}
+
+bool ctkModuleDescriptionValidator::validateInput()
+{
+  _errorStr.clear();
+
+  if (!_input)
+  {
+    _errorStr = "No input set for validation.";
+    return false;
+  }
+
+  QIODevice* inputSchema = _inputSchema;
+  QScopedPointer<QIODevice> defaultInputSchema(new QFile(":/ctkModuleDescription.xsd"));
+  if (!inputSchema)
+  {
+    inputSchema = defaultInputSchema.data();
+    inputSchema->open(QIODevice::ReadOnly);
+  }
+
+  _MessageHandler errorHandler;
+
+  QXmlSchema schema;
+  schema.setMessageHandler(&errorHandler);
+
+  if (!schema.load(inputSchema))
+  {
+    QString msg("Invalid input schema at line %1, column %2: %3");
+    _errorStr = msg.arg(errorHandler.line()).arg(errorHandler.column()).arg(errorHandler.statusMessage());
+    return false;
+  }
+
+  QXmlSchemaValidator validator(schema);
+
+  if (!validator.validate(_input))
+  {
+    QString msg("Error validating CLI XML description, at line %1, column %2: %3");
+    _errorStr = msg.arg(errorHandler.line()).arg(errorHandler.column())
+                .arg(errorHandler.statusMessage());
+    return false;
+  }
+
+  return true;
+}
+
+bool ctkModuleDescriptionValidator::validateOutput()
+{
+  _errorStr.clear();
+  _output.clear();
+
+  if (!_input)
+  {
+    _errorStr = "No input set for deriving an output.";
+    return false;
+  }
+  else if (!(_input->openMode() & QIODevice::ReadOnly))
+  {
+    _input->open(QIODevice::ReadOnly);
+  }
+  _input->reset();
+
+  _MessageHandler msgHandler;
+
+  QXmlQuery xslTransform(QXmlQuery::XSLT20);
+  xslTransform.setMessageHandler(&msgHandler);
+  if (!xslTransform.setFocus(_input))
+  {
+    QString msg("Error transforming CLI XML description: %1");
+    _errorStr = msg.arg(msgHandler.statusMessage());
+    return false;
+  }
+
+  QIODevice* transformation = _transformation;
+  QScopedPointer<QIODevice> defaultTransform(new QFile(":/ctkQtModuleDescription.xsl"));
+  if (!transformation)
+  {
+    transformation = defaultTransform.data();
+    transformation->open(QIODevice::ReadOnly);
+  }
+  xslTransform.setQuery(transformation);
+
+  if (!xslTransform.evaluateTo(&_output))
+  {
+    _output.clear();
+    QString msg("Error transforming CLI XML description, at line %1, column %2: %3");
+    _errorStr = msg.arg(msgHandler.line()).arg(msgHandler.column())
+                .arg(msgHandler.statusMessage());
+    return false;
+  }
+
+  QIODevice* outputSchema = _outputSchema;
+
+  // If there is no custom schema for validating the output, we just return.
+  // The QtDesigner.xsd schema below (which should be the default) exhausts
+  // the memory during validation.
+  if (!outputSchema) return true;
+
+  QScopedPointer<QIODevice> defaultOutputSchema(new QFile(":/QtDesigner.xsd"));
+  if (!outputSchema)
+  {
+    outputSchema = defaultOutputSchema.data();
+    outputSchema->open(QIODevice::ReadOnly);
+  }
+  outputSchema->reset();
+
+  QXmlSchema schema;
+  schema.setMessageHandler(&msgHandler);
+
+  if (!schema.load(outputSchema))
+  {
+    QString msg("Invalid output schema at line %1, column %2: %3");
+    _errorStr = msg.arg(msgHandler.line()).arg(msgHandler.column()).arg(msgHandler.statusMessage());
+    return false;
+  }
+
+  QXmlSchemaValidator validator(schema);
+  validator.setMessageHandler(&msgHandler);
+
+  QByteArray outputData;
+  outputData.append(_output);
+
+  if (!validator.validate(outputData))
+  {
+    QString msg("Error validating transformed CLI XML description, at line %1, column %2: %3");
+    _errorStr = msg.arg(msgHandler.line()).arg(msgHandler.column())
+                .arg(msgHandler.statusMessage());
+    return false;
+  }
+
+  return true;
+}
+
+bool ctkModuleDescriptionValidator::error() const
+{
+  return !_errorStr.isEmpty();
+}
+
+QString ctkModuleDescriptionValidator::errorString() const
+{
+  return _errorStr;
+}

+ 64 - 0
Libs/ModuleDescription/ctkModuleDescriptionValidator.h

@@ -0,0 +1,64 @@
+/*=============================================================================
+  
+  Library: CTK
+  
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+    
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  
+=============================================================================*/
+
+#ifndef CTKMODULEDESCRIPTIONVALIDATOR_H
+#define CTKMODULEDESCRIPTIONVALIDATOR_H
+
+#include <ctkModuleDescriptionExport.h>
+
+#include <QString>
+
+class QIODevice;
+
+class CTK_MODULDESC_EXPORT ctkModuleDescriptionValidator
+{
+
+public:
+
+  ctkModuleDescriptionValidator(QIODevice* input = 0);
+
+  void setInput(QIODevice* input);
+  QString output();
+
+  void setInputSchema(QIODevice* input);
+  void setOutputSchema(QIODevice* output);
+
+  void setXSLTransformation(QIODevice* transformation);
+
+  bool validate();
+  bool validateInput();
+  bool validateOutput();
+
+  bool error() const;
+  QString errorString() const;
+
+private:
+
+  QIODevice* _input;
+  QIODevice* _inputSchema;
+  QIODevice* _outputSchema;
+  QIODevice* _transformation;
+
+  QString _output;
+  QString _errorStr;
+};
+
+#endif // CTKMODULEDESCRIPTIONVALIDATOR_H

+ 43 - 0
Libs/ModuleDescription/ctkModuleManager.cpp

@@ -0,0 +1,43 @@
+/*=============================================================================
+  
+  Library: CTK
+  
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+    
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  
+=============================================================================*/
+
+#include "ctkModuleManager.h"
+
+#include "ctkModuleObjectHierarchyReader.h"
+
+#include <QDebug>
+
+ctkModuleManager::ctkModuleManager()
+{
+}
+
+QString ctkModuleManager::createCommandLine(QObject *hierarchy)
+{
+  ctkModuleObjectHierarchyReader reader(hierarchy);
+
+  QString cmdLine;
+  while(reader.readNextParameter())
+  {
+    qDebug() << "Found parameter:" << reader.name() << "," << reader.value();
+  }
+
+  return cmdLine;
+}

+ 39 - 0
Libs/ModuleDescription/ctkModuleManager.h

@@ -0,0 +1,39 @@
+/*=============================================================================
+  
+  Library: CTK
+  
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+    
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  
+=============================================================================*/
+
+#ifndef CTKMODULEMANAGER_H
+#define CTKMODULEMANAGER_H
+
+#include <QString>
+
+#include <ctkModuleDescriptionExport.h>
+
+class QObject;
+
+class CTK_MODULDESC_EXPORT ctkModuleManager
+{
+public:
+  ctkModuleManager();
+
+  static QString createCommandLine(QObject* hierarchy);
+};
+
+#endif // CTKMODULEMANAGER_H

+ 243 - 0
Libs/ModuleDescription/ctkModuleObjectHierarchyReader.cpp

@@ -0,0 +1,243 @@
+/*=============================================================================
+  
+  Library: CTK
+  
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+    
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  
+=============================================================================*/
+
+#include "ctkModuleObjectHierarchyReader.h"
+
+#include <QObject>
+#include <QStack>
+#include <QVariant>
+
+namespace {
+
+static QString PREFIX_EXECUTABLE = "executable:";
+static QString PREFIX_PARAMETER_GROUP = "paramGroup:";
+static QString PREFIX_PARAMETER = "parameter:";
+
+}
+
+class ctkModuleObjectHierarchyReaderPrivate
+{
+public:
+
+  ctkModuleObjectHierarchyReaderPrivate(QObject* root)
+    : rootObject(root), currentObject(0), currentToken(ctkModuleObjectHierarchyReader::NoToken),
+      atEnd(false)
+  {
+  }
+
+  QVariant property(const QString& propName) const
+  {
+    if (currentObject == 0) return QString();
+
+    QString prefixedName;
+    switch(currentToken)
+    {
+    case ctkModuleObjectHierarchyReader::Executable: prefixedName = PREFIX_EXECUTABLE + propName;
+    case ctkModuleObjectHierarchyReader::ParameterGroup: prefixedName = PREFIX_PARAMETER_GROUP + propName;
+    case ctkModuleObjectHierarchyReader::Parameter: prefixedName = PREFIX_PARAMETER + propName;
+    default: ;
+    }
+
+    return currentObject->property(qPrintable(prefixedName));
+  }
+
+  ctkModuleObjectHierarchyReader::TokenType token(QObject* obj)
+  {
+    if (obj == 0) return ctkModuleObjectHierarchyReader::NoToken;
+    QString name = obj->objectName();
+    if (name.startsWith(PREFIX_EXECUTABLE)) return ctkModuleObjectHierarchyReader::Executable;
+    if (name.startsWith(PREFIX_PARAMETER_GROUP)) return ctkModuleObjectHierarchyReader::ParameterGroup;
+    if (name.startsWith(PREFIX_PARAMETER)) return ctkModuleObjectHierarchyReader::Parameter;
+    return ctkModuleObjectHierarchyReader::NoToken;
+  }
+
+  bool setCurrent(QObject* obj)
+  {
+    ctkModuleObjectHierarchyReader::TokenType t = token(obj);
+    if (t != ctkModuleObjectHierarchyReader::NoToken)
+    {
+      currentObject = obj;
+      currentToken = t;
+      return true;
+    }
+    return false;
+  }
+
+  QObject* rootObject;
+  QObject* currentObject;
+
+  ctkModuleObjectHierarchyReader::TokenType currentToken;
+  bool atEnd;
+
+  QStack<QObject*> objectStack;
+};
+
+ctkModuleObjectHierarchyReader::ctkModuleObjectHierarchyReader(QObject *root)
+  : d(new ctkModuleObjectHierarchyReaderPrivate(root))
+{
+}
+
+ctkModuleObjectHierarchyReader::~ctkModuleObjectHierarchyReader()
+{
+}
+
+void ctkModuleObjectHierarchyReader::setRootObject(QObject* root)
+{
+  d->rootObject = root;
+  clear();
+}
+
+void ctkModuleObjectHierarchyReader::clear()
+{
+  d->currentToken = NoToken;
+  d->currentObject = 0;
+  d->objectStack.clear();
+}
+
+bool ctkModuleObjectHierarchyReader::atEnd() const
+{
+  return d->atEnd || d->rootObject == 0;
+}
+
+bool ctkModuleObjectHierarchyReader::isParameterGroup() const
+{
+  return d->currentToken == ParameterGroup;
+}
+
+bool ctkModuleObjectHierarchyReader::isParameter() const
+{
+  return d->currentToken == Parameter;
+}
+
+QString ctkModuleObjectHierarchyReader::name() const
+{
+  if (d->currentObject == 0) return QString();
+  switch(d->currentToken)
+  {
+  case Executable: return d->currentObject->objectName().mid(PREFIX_EXECUTABLE.size());
+  case ParameterGroup: return d->currentObject->objectName().mid(PREFIX_PARAMETER_GROUP.size());
+  case Parameter: return d->currentObject->objectName().mid(PREFIX_PARAMETER.size());
+  default: return QString();
+  }
+}
+
+QString ctkModuleObjectHierarchyReader::label() const
+{
+  if (d->currentObject == 0) return QString();
+  switch(d->currentToken)
+  {
+  case Executable: return d->currentObject->objectName().mid(PREFIX_EXECUTABLE.size());
+  case ParameterGroup: return d->property("title").toString();
+  case Parameter: return property("label").toString();
+  default: return QString();
+  }
+}
+#include <QDebug>
+QString ctkModuleObjectHierarchyReader::value() const
+{
+  QString valProp = property("valueProperty").toString();
+  qDebug() << "valProp:" << valProp;
+  return property(valProp).toString();
+}
+
+QVariant ctkModuleObjectHierarchyReader::property(const QString &propName) const
+{
+  if (d->currentObject == 0) return QVariant();
+
+  // First try to get a prefixed property
+  QVariant res = d->property(propName);
+  // Try to get a property with the original name
+  if (!res.isValid()) res = d->currentObject->property(qPrintable(propName));
+  return res;
+}
+
+ctkModuleObjectHierarchyReader::TokenType ctkModuleObjectHierarchyReader::readNext() const
+{
+  if (d->atEnd) return NoToken;
+
+  QObject* curr = 0;
+  if (d->currentObject == 0)
+  {
+    curr = d->rootObject;
+    if (d->setCurrent(curr)) return d->currentToken;
+  }
+  else
+  {
+    curr = d->currentObject;
+  }
+
+  while (true)
+  {
+    if (curr)
+    {
+      QObjectList children = curr->children();
+      QListIterator<QObject*> i(children);
+      i.toBack();
+      while (i.hasPrevious())
+      {
+        d->objectStack.push(i.previous());
+      }
+      if (children.isEmpty())
+      {
+        curr = 0;
+      }
+      else
+      {
+        curr = d->objectStack.pop();
+        if (d->setCurrent(curr)) return d->currentToken;
+      }
+      continue;
+    }
+
+    if (d->objectStack.isEmpty()) break;
+    curr = d->objectStack.pop();
+    if (d->setCurrent(curr)) return d->currentToken;
+  }
+
+  d->atEnd = true;
+  d->currentObject = 0;
+  d->currentToken = NoToken;
+
+  return NoToken;
+}
+
+bool ctkModuleObjectHierarchyReader::readNextExecutable() const
+{
+  while (!(readNext() == Executable || d->atEnd));
+  return !d->atEnd;
+}
+
+bool ctkModuleObjectHierarchyReader::readNextParameterGroup() const
+{
+  while (!(readNext() == ParameterGroup || d->atEnd));
+  return !d->atEnd;
+}
+
+bool ctkModuleObjectHierarchyReader::readNextParameter() const
+{
+  while (!(readNext() == Parameter || d->atEnd));
+  return !d->atEnd;
+}
+
+ctkModuleObjectHierarchyReader::TokenType ctkModuleObjectHierarchyReader::tokenType() const
+{
+  return d->currentToken;
+}

+ 77 - 0
Libs/ModuleDescription/ctkModuleObjectHierarchyReader.h

@@ -0,0 +1,77 @@
+/*=============================================================================
+  
+  Library: CTK
+  
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+    
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  
+=============================================================================*/
+
+#ifndef CTKMODULEOBJECTHIERARCHYREADER_H
+#define CTKMODULEOBJECTHIERARCHYREADER_H
+
+#include <QString>
+#include <QScopedPointer>
+#include <QVariant>
+
+#include <ctkModuleDescriptionExport.h>
+
+class QObject;
+
+class ctkModuleObjectHierarchyReaderPrivate;
+
+class CTK_MODULDESC_EXPORT ctkModuleObjectHierarchyReader
+{
+
+public:
+
+  enum TokenType {
+    NoToken,
+    Executable,
+    ParameterGroup,
+    Parameter
+  };
+
+  ctkModuleObjectHierarchyReader(QObject* root = 0);
+  ~ctkModuleObjectHierarchyReader();
+
+  void setRootObject(QObject* root);
+  void clear();
+
+  bool atEnd() const;
+
+  bool isParameterGroup() const;
+  bool isParameter() const;
+
+  QString name() const;
+  QString label() const;
+  QString value() const;
+
+  QVariant property(const QString& propName) const;
+
+  TokenType readNext() const;
+  bool readNextExecutable() const;
+  bool readNextParameterGroup() const;
+  bool readNextParameter() const;
+
+  TokenType tokenType() const;
+
+private:
+
+  QScopedPointer<ctkModuleObjectHierarchyReaderPrivate> d;
+
+};
+
+#endif // CTKMODULEOBJECTHIERARCHYREADER_H