From eaf0bca8fed55cb14b019dc97e48c80d5b36c8cf Mon Sep 17 00:00:00 2001 From: Kimmo Varis Date: Thu, 3 Feb 2011 21:40:35 +0200 Subject: [PATCH] GUI: Write XML version 2. Ticket #2521 (GUI: Add XML format 2 support) --- gui/gui.pro | 2 + gui/mainwindow.cpp | 10 ++- gui/report.h | 1 + gui/resultsview.cpp | 4 + gui/xmlreportv2.cpp | 200 ++++++++++++++++++++++++++++++++++++++++++++ gui/xmlreportv2.h | 96 +++++++++++++++++++++ 6 files changed, 311 insertions(+), 2 deletions(-) create mode 100644 gui/xmlreportv2.cpp create mode 100644 gui/xmlreportv2.h diff --git a/gui/gui.pro b/gui/gui.pro index 54952ac6c..5df4c2bab 100644 --- a/gui/gui.pro +++ b/gui/gui.pro @@ -69,6 +69,7 @@ HEADERS += mainwindow.h \ report.h \ txtreport.h \ xmlreport.h \ + xmlreportv2.h \ translationhandler.h \ csvreport.h \ logview.h \ @@ -96,6 +97,7 @@ SOURCES += main.cpp \ report.cpp \ txtreport.cpp \ xmlreport.cpp \ + xmlreportv2.cpp \ translationhandler.cpp \ csvreport.cpp \ logview.cpp \ diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 72cd10091..e052db341 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -579,7 +579,7 @@ void MainWindow::ShowAuthors() void MainWindow::Save() { QString selectedFilter; - QString filter(tr("XML files (*.xml);;Text files (*.txt);;CSV files (*.csv)")); + QString filter(tr("XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv)")); QString selectedFile = QFileDialog::getSaveFileName(this, tr("Save the report file"), QString(), @@ -589,12 +589,18 @@ void MainWindow::Save() if (!selectedFile.isEmpty()) { Report::Type type = Report::TXT; - if (selectedFilter == tr("XML files (*.xml)")) + if (selectedFilter == tr("XML files version 1 (*.xml)")) { type = Report::XML; if (!selectedFile.endsWith(".xml", Qt::CaseInsensitive)) selectedFile += ".xml"; } + else if (selectedFilter == tr("XML files version 2 (*.xml)")) + { + type = Report::XMLV2; + if (!selectedFile.endsWith(".xml", Qt::CaseInsensitive)) + selectedFile += ".xml"; + } else if (selectedFilter == tr("Text files (*.txt)")) { type = Report::TXT; diff --git a/gui/report.h b/gui/report.h index 681bdecf6..ace33c4b6 100644 --- a/gui/report.h +++ b/gui/report.h @@ -39,6 +39,7 @@ public: { TXT, XML, + XMLV2, CSV, }; diff --git a/gui/resultsview.cpp b/gui/resultsview.cpp index ab1234f46..e74d7b982 100644 --- a/gui/resultsview.cpp +++ b/gui/resultsview.cpp @@ -31,6 +31,7 @@ #include "report.h" #include "txtreport.h" #include "xmlreport.h" +#include "xmlreportv2.h" #include "csvreport.h" #include "applicationlist.h" #include "checkstatistics.h" @@ -132,6 +133,9 @@ void ResultsView::Save(const QString &filename, Report::Type type) case Report::XML: report = new XmlReport(filename, this); break; + case Report::XMLV2: + report = new XmlReportV2(filename, this); + break; } if (report) diff --git a/gui/xmlreportv2.cpp b/gui/xmlreportv2.cpp new file mode 100644 index 000000000..165811b2e --- /dev/null +++ b/gui/xmlreportv2.cpp @@ -0,0 +1,200 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2011 Daniel Marjamäki and Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include "report.h" +#include "erroritem.h" +#include "xmlreportv2.h" +#include "cppcheck.h" + +static const char ResultElementName[] = "results"; +static const char CppcheckElementName[] = "cppcheck"; +static const char ErrorElementName[] = "error"; +static const char ErrorsElementName[] = "errors"; +static const char LocationElementName[] = "location"; +static const char FilenameAttribute[] = "file"; +static const char LineAttribute[] = "line"; +static const char IdAttribute[] = "id"; +static const char SeverityAttribute[] = "severity"; +static const char MsgAttribute[] = "msg"; +static const char VersionAttribute[] = "version"; +static const char VerboseAttribute[] = "verbose"; + +XmlReportV2::XmlReportV2(const QString &filename, QObject * parent) : + Report(filename, parent), + mXmlReader(NULL), + mXmlWriter(NULL) +{ +} + +XmlReportV2::~XmlReportV2() +{ + delete mXmlReader; + delete mXmlWriter; + Close(); +} + +bool XmlReportV2::Create() +{ + bool success = false; + if (Report::Create()) + { + mXmlWriter = new QXmlStreamWriter(Report::GetFile()); + success = true; + } + return success; +} + +bool XmlReportV2::Open() +{ + bool success = false; + if (Report::Open()) + { + mXmlReader = new QXmlStreamReader(Report::GetFile()); + success = true; + } + return success; +} + +void XmlReportV2::WriteHeader() +{ + mXmlWriter->setAutoFormatting(true); + mXmlWriter->writeStartDocument(); + mXmlWriter->writeStartElement(ResultElementName); + mXmlWriter->writeAttribute(VersionAttribute, QString::number(2)); + mXmlWriter->writeStartElement(CppcheckElementName); + mXmlWriter->writeAttribute(VersionAttribute, QString(CppCheck::version())); + mXmlWriter->writeEndElement(); + mXmlWriter->writeStartElement(ErrorsElementName); +} + +void XmlReportV2::WriteFooter() +{ + mXmlWriter->writeEndElement(); // errors + mXmlWriter->writeEndElement(); // results + mXmlWriter->writeEndDocument(); +} + +void XmlReportV2::WriteError(const ErrorItem &error) +{ + /* + Error example from the core program in xml + + + + + */ + + mXmlWriter->writeStartElement(ErrorElementName); + mXmlWriter->writeAttribute(IdAttribute, error.id); + mXmlWriter->writeAttribute(SeverityAttribute, error.severity); + mXmlWriter->writeAttribute(MsgAttribute, error.summary); + mXmlWriter->writeAttribute(VerboseAttribute, error.message); + + for (int i = 0; i < error.files.count(); i++) + { + mXmlWriter->writeStartElement(LocationElementName); + + const QString file = QDir::toNativeSeparators(error.files[i]); + mXmlWriter->writeAttribute(FilenameAttribute, file); + const QString line = QString::number(error.lines[i]); + mXmlWriter->writeAttribute(LineAttribute, line); + + mXmlWriter->writeEndElement(); + } + + mXmlWriter->writeEndElement(); +} + +QList XmlReportV2::Read() +{ + QList errors; + bool insideResults = false; + if (!mXmlReader) + { + qDebug() << "You must Open() the file before reading it!"; + return errors; + } + while (!mXmlReader->atEnd()) + { + switch (mXmlReader->readNext()) + { + case QXmlStreamReader::StartElement: + if (mXmlReader->name() == ResultElementName) + insideResults = true; + + // Read error element from inside result element + if (insideResults && mXmlReader->name() == ErrorElementName) + { + ErrorLine line = ReadError(mXmlReader); + errors.append(line); + } + break; + + case QXmlStreamReader::EndElement: + if (mXmlReader->name() == ResultElementName) + insideResults = false; + break; + + // Not handled + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Characters: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } + return errors; +} + +ErrorLine XmlReportV2::ReadError(QXmlStreamReader *reader) +{ + ErrorLine line; + if (reader->name().toString() == ErrorElementName) + { + QXmlStreamAttributes attribs = reader->attributes(); + line.file = attribs.value("", FilenameAttribute).toString(); + line.line = attribs.value("", LineAttribute).toString().toUInt(); + line.id = attribs.value("", IdAttribute).toString(); + line.severity = attribs.value("", SeverityAttribute).toString(); + + // NOTE: This dublicates the message to Summary-field. But since + // old XML format doesn't have separate summary and verbose messages + // we must add same message to both data so it shows up in GUI. + // Check if there is full stop and cut the summary to it. + QString summary = attribs.value("", MsgAttribute).toString(); + const int ind = summary.indexOf('.'); + if (ind != -1) + summary = summary.left(ind + 1); + line.summary = summary; + line.message = attribs.value("", MsgAttribute).toString(); + } + return line; +} diff --git a/gui/xmlreportv2.h b/gui/xmlreportv2.h new file mode 100644 index 000000000..33f96349d --- /dev/null +++ b/gui/xmlreportv2.h @@ -0,0 +1,96 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2011 Daniel Marjamäki and Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef XML_REPORTV2_H +#define XML_REPORTV2_H + +#include +#include +#include +#include +#include +#include +#include "report.h" + +/// @addtogroup GUI +/// @{ + + +/** +* @brief XML file report version 2. +* This report outputs XML-formatted report. The XML format must match command +* line version's XML output. +*/ +class XmlReportV2 : public Report +{ +public: + XmlReportV2(const QString &filename, QObject * parent = 0); + virtual ~XmlReportV2(); + + /** + * @brief Create the report (file). + * @return true if succeeded, false if file could not be created. + */ + virtual bool Create(); + + /** + * @brief Open existing report file. + */ + bool Open(); + + /** + * @brief Write report header. + */ + virtual void WriteHeader(); + + /** + * @brief Write report footer. + */ + virtual void WriteFooter(); + + /** + * @brief Write error to report. + * @param error Error data. + */ + virtual void WriteError(const ErrorItem &error); + + /** + * @brief Read contents of the report file. + */ + QList Read(); + +protected: + /** + * @brief Read and parse error item from XML stream. + * @param reader XML stream reader to use. + */ + ErrorLine ReadError(QXmlStreamReader *reader); + +private: + /** + * @brief XML stream reader for reading the report in XML format. + */ + QXmlStreamReader *mXmlReader; + + /** + * @brief XML stream writer for writing the report in XML format. + */ + QXmlStreamWriter *mXmlWriter; +}; +/// @} +#endif // XML_REPORTV2_H