File indexing completed on 2022-11-22 13:51:27

0001 /* This file is part of KDevelop
0002    Copyright 2006-2008 Hamish Rodda <rodda@kde.org>
0003    Copyright 2011 Mathieu Lornac <mathieu.lornac@gmail.com>
0004    Copyright 2011 Damien Coppel <damien.coppel@gmail.com>
0005    Copyright 2011 Lionel Duc <lionel.data@gmail.com>
0006    Copyright 2015 Laszlo Kis-Adam <laszlo.kis-adam@kdemail.net>
0007    Copyright 2016-2017 Anton Anikin <anton@anikin.xyz>
0008 
0009    This program is free software; you can redistribute it and/or
0010    modify it under the terms of the GNU General Public
0011    License as published by the Free Software Foundation; either
0012    version 2 of the License, or (at your option) any later version.
0013 
0014    This program is distributed in the hope that it will be useful,
0015    but WITHOUT ANY WARRANTY; without even the implied warranty of
0016    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0017    General Public License for more details.
0018 
0019    You should have received a copy of the GNU General Public License
0020    along with this program; see the file COPYING.  If not, write to
0021    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0022    Boston, MA 02110-1301, USA.
0023 */
0024 
0025 #include "xmlparser.h"
0026 
0027 #include "debug.h"
0028 #include "xmlerror.h"
0029 #include "utils.h"
0030 
0031 #include <KLocalizedString>
0032 #include <KMessageBox>
0033 
0034 #include <QStack>
0035 #include <QXmlStreamReader>
0036 
0037 namespace Valgrind
0038 {
0039 
0040 namespace XmlParser
0041 {
0042 
0043 class Parser : public QXmlStreamReader
0044 {
0045 public:
0046     explicit Parser(const QString& toolName);
0047     virtual ~Parser();
0048 
0049     QVector<KDevelop::IProblem::Ptr> parse(bool showInstructionPointer);
0050 
0051 private:
0052     void startElement();
0053     void endElement(QVector<KDevelop::IProblem::Ptr>& problems, bool showInstructionPointer);
0054 
0055     void clear();
0056 
0057     enum State {
0058         Unknown,
0059         Session,
0060         Status,
0061         Preamble,
0062         Error,
0063         Stack,
0064         Frame,
0065 
0066         // DRD tool
0067         OtherSegmentStart,
0068         OtherSegmentEnd
0069     };
0070 
0071     QString m_toolName;
0072 
0073     QStack<State> m_stateStack;
0074 
0075     QString m_name;
0076     QString m_value;
0077 
0078     XmlParser::Frame* m_frame;
0079     XmlParser::Stack* m_stack;
0080     XmlParser::OtherSegment* m_otherSegment;
0081     XmlParser::Error* m_error;
0082 };
0083 
0084 inline QString errorXmlName() { return QStringLiteral("error"); }
0085 inline QString stackXmlName() { return QStringLiteral("stack"); }
0086 inline QString frameXmlName() { return QStringLiteral("frame"); }
0087 
0088 inline QString otherSegmentStartXmlName() { return QStringLiteral("other_segment_start"); }
0089 inline QString otherSegmentEndXmlName()   { return QStringLiteral("other_segment_end"); }
0090 
0091 Parser::Parser(const QString& toolName)
0092     : m_toolName(toolName)
0093     , m_frame(nullptr)
0094     , m_stack(nullptr)
0095     , m_otherSegment(nullptr)
0096     , m_error(new XmlParser::Error)
0097 {
0098 }
0099 
0100 Parser::~Parser()
0101 {
0102     delete m_error;
0103 }
0104 
0105 void Parser::clear()
0106 {
0107     m_stateStack.clear();
0108     m_name.clear();
0109     m_value.clear();
0110 }
0111 
0112 void Parser::startElement()
0113 {
0114     State newState = Unknown;
0115 
0116     if (m_name == QStringLiteral("valgrindoutput")) {
0117         newState = Session;
0118     }
0119 
0120     else if (m_name == QStringLiteral("status")) {
0121         newState = Status;
0122     }
0123 
0124     else if (m_name == QStringLiteral("preamble")) {
0125         newState = Preamble;
0126     }
0127 
0128     else if (m_name == errorXmlName()) {
0129         newState = Error;
0130         m_error->clear();
0131     }
0132 
0133     else if (m_name == stackXmlName()) {
0134         newState = Stack;
0135 
0136         // Useful stacks are inside error or other_segment_begin/end
0137         if (m_stateStack.top() == Error) {
0138             m_stack = m_error->addStack();
0139         } else if (m_stateStack.top() == OtherSegmentStart ||
0140                    m_stateStack.top() == OtherSegmentEnd) {
0141 
0142             Q_ASSERT(m_otherSegment);
0143             m_stack = m_otherSegment->addStack();
0144         }
0145     }
0146 
0147     else if (m_name == frameXmlName()) {
0148         newState = Frame;
0149         if (m_stack) {
0150             m_frame = m_stack->addFrame();
0151         }
0152     }
0153 
0154     else if (m_name == otherSegmentStartXmlName()) {
0155         newState = OtherSegmentStart;
0156         m_otherSegment = m_error->addOtherSegment(true);
0157     }
0158 
0159     else if (m_name == otherSegmentEndXmlName()) {
0160         newState = OtherSegmentEnd;
0161         m_otherSegment = m_error->addOtherSegment(false);
0162     }
0163 
0164     else {
0165         m_stateStack.push(m_stateStack.top());
0166         return;
0167     }
0168 
0169     m_stateStack.push(newState);
0170     return;
0171 }
0172 
0173 void Parser::endElement(QVector<KDevelop::IProblem::Ptr>& problems, bool showInstructionPointer)
0174 {
0175     State state = m_stateStack.pop();
0176 
0177     switch (state) {
0178 
0179     case Error:
0180         if (m_name == errorXmlName()) {
0181             problems.append(m_error->toIProblem(m_toolName, showInstructionPointer));
0182         } else {
0183             m_error->setValue(m_name, m_value);
0184         }
0185         break;
0186 
0187     case Stack:
0188         if (m_stack && m_name == stackXmlName()) {
0189             m_stack = nullptr;
0190         }
0191         break;
0192 
0193     case Frame:
0194         if (m_frame) {
0195             if (m_name == frameXmlName()) {
0196                 m_frame = nullptr;
0197             } else {
0198                 m_frame->setValue(m_name, m_value);
0199             }
0200         }
0201         break;
0202 
0203     case OtherSegmentStart:
0204         if (m_name == otherSegmentStartXmlName()) {
0205             m_otherSegment = nullptr;
0206         }
0207         break;
0208 
0209     case OtherSegmentEnd:
0210         if (m_name == otherSegmentEndXmlName()) {
0211             m_otherSegment = nullptr;
0212         }
0213         break;
0214 
0215     default:
0216         break;
0217     }
0218 }
0219 
0220 QVector<KDevelop::IProblem::Ptr> Parser::parse(bool showInstructionPointer)
0221 {
0222     QVector<KDevelop::IProblem::Ptr> problems;
0223 
0224     while (!atEnd()) {
0225         switch (readNext()) {
0226 
0227         case StartDocument:
0228             clear();
0229             break;
0230 
0231         case StartElement:
0232             m_name = name().toString();
0233             m_value.clear();
0234             startElement();
0235             break;
0236 
0237         case EndElement:
0238             m_name = name().toString();
0239             endElement(problems, showInstructionPointer);
0240             break;
0241 
0242         case Characters:
0243             m_value += text();
0244             break;
0245 
0246         default:
0247             break;
0248         }
0249     }
0250 
0251     if (hasError()) {
0252         switch (error()) {
0253 
0254         case CustomError:
0255         case UnexpectedElementError:
0256         case NotWellFormedError:
0257             KMessageBox::error(activeMainWindow(),
0258                                i18n("Valgrind XML Parsing: error at line %1, column %2: %3",
0259                                     lineNumber(),
0260                                     columnNumber(),
0261                                     errorString()),
0262                                i18n("Valgrind Error"));
0263             break;
0264 
0265         case NoError:
0266         case PrematureEndOfDocumentError:
0267             break;
0268         }
0269     }
0270 
0271     return problems;
0272 }
0273 
0274 }
0275 
0276 QVector<KDevelop::IProblem::Ptr> parseXml(const QString& toolName, const QString& xmlData, bool showInstructionPointer)
0277 {
0278     XmlParser::Parser parser(toolName);
0279     parser.addData(xmlData);
0280 
0281     return parser.parse(showInstructionPointer);
0282 }
0283 
0284 }