File indexing completed on 2024-05-12 03:47:46

0001 /*
0002     File                 : XmlStreamReader.cpp
0003     Project              : LabPlot
0004     Description          : XML stream parser that supports errors and warnings
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2009 Tilman Benkert <thzs@gmx.net>
0007     SPDX-FileCopyrightText: 2015-2016 Alexander Semke <alexander.semke@web.de>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #include "backend/lib/XmlStreamReader.h"
0013 #include <KLocalizedString>
0014 
0015 /**
0016  * \class XmlStreamReader
0017  * \brief XML stream parser that supports errors as well as warnings.
0018  * This class also adds line and column numbers to the error message.
0019  */
0020 XmlStreamReader::XmlStreamReader() = default;
0021 
0022 XmlStreamReader::XmlStreamReader(QIODevice* device)
0023     : QXmlStreamReader(device) {
0024 }
0025 
0026 XmlStreamReader::XmlStreamReader(const QByteArray& data)
0027     : QXmlStreamReader(data) {
0028 }
0029 
0030 XmlStreamReader::XmlStreamReader(const QString& data)
0031     : QXmlStreamReader(data) {
0032 }
0033 
0034 XmlStreamReader::XmlStreamReader(const char* data)
0035     : QXmlStreamReader(data) {
0036 }
0037 
0038 const QStringList& XmlStreamReader::warningStrings() const {
0039     return m_warnings;
0040 }
0041 
0042 /*
0043  * returns the human readable string for the missing CAS plugins in case
0044  * the project has some CAS content but the application was either not compiled with
0045  * CAS/Cantor support or the correspongind plugins for the required backends are missing.
0046  *
0047  * The returned text is in the form "Octave" or "Octave and Maxima" or "Octave, Maxima and Python", etc.
0048  */
0049 QString XmlStreamReader::missingCASWarning() const {
0050     const int count = m_missingCASPlugins.count();
0051     if (count == 1)
0052         return m_missingCASPlugins.constFirst();
0053     else {
0054         QString msg;
0055         for (int i = 0; i < count; ++i) {
0056             if (!msg.isEmpty()) {
0057                 if (i == count - 1)
0058                     msg += QLatin1Char(' ') + i18n("and") + QLatin1Char(' ');
0059                 else
0060                     msg += QLatin1String(", ");
0061             }
0062             msg += m_missingCASPlugins.at(i);
0063         }
0064         return msg;
0065     }
0066 }
0067 
0068 bool XmlStreamReader::hasWarnings() const {
0069     return !m_warnings.isEmpty();
0070 }
0071 
0072 bool XmlStreamReader::hasMissingCASWarnings() const {
0073     return !m_missingCASPlugins.isEmpty();
0074 }
0075 
0076 void XmlStreamReader::setFailedCASMissing(bool value) {
0077     m_failedCASMissing = value;
0078 }
0079 
0080 /*!
0081  * returns \c true if the loading of an project object failed because
0082  * of the missing CAS functionality (no CAS support or missing CAS plugin).
0083  * returns \c false if the loadign failed because of other reasons
0084  * like broken XML or missing important and required attributes.
0085  */
0086 bool XmlStreamReader::failedCASMissing() const {
0087     return m_failedCASMissing;
0088 }
0089 
0090 void XmlStreamReader::raiseError(const QString& message) {
0091     QXmlStreamReader::raiseError(i18n("line %1, column %2: %3", lineNumber(), columnNumber(), message));
0092 }
0093 
0094 void XmlStreamReader::raiseWarning(const QString& message) {
0095     m_warnings.append(i18n("line %1, column %2: %3", lineNumber(), columnNumber(), message));
0096 }
0097 
0098 void XmlStreamReader::raiseMissingAttributeWarning(const QString& attribute) {
0099     static auto warning = i18n("Attribute '%1' missing or empty, default value is used");
0100     m_warnings.append(i18n("line %1, column %2: %3", lineNumber(), columnNumber(), warning.arg(attribute)));
0101 }
0102 
0103 void XmlStreamReader::raiseUnknownElementWarning() {
0104     static auto warning = i18n("unknown element '%1'");
0105     m_warnings.append(i18n("line %1, column %2: %3", lineNumber(), columnNumber(), warning.arg(name())));
0106 }
0107 
0108 void XmlStreamReader::raiseMissingCASWarning(const QString& name) {
0109     m_missingCASPlugins.append(name);
0110 }
0111 
0112 /*!
0113  * Go to the next start or end element tag
0114  * If the end of the document is reached, an error is raised.
0115  * \return false if end of document reached, otherwise true
0116  */
0117 bool XmlStreamReader::skipToNextTag() {
0118     if (atEnd()) {
0119         raiseError(i18n("unexpected end of document"));
0120         return false;
0121     }
0122 
0123     do {
0124         readNext();
0125     } while (!(isStartElement() || isEndElement() || atEnd()));
0126 
0127     if (atEnd()) {
0128         raiseError(i18n("unexpected end of document"));
0129         return false;
0130     }
0131 
0132     return true;
0133 }
0134 
0135 /*!
0136  * Go to the end element tag of the current element
0137  * If the end of the document is reached, an error is raised.
0138  * \return false if end of document reached, otherwise true
0139  */
0140 bool XmlStreamReader::skipToEndElement() {
0141     int depth = 1;
0142     if (atEnd()) {
0143         raiseError(i18n("unexpected end of document"));
0144         return false;
0145     }
0146 
0147     do {
0148         readNext();
0149         if (isEndElement())
0150             depth--;
0151         if (isStartElement())
0152             depth++;
0153     } while (!((isEndElement() && depth == 0) || atEnd()));
0154 
0155     if (atEnd()) {
0156         raiseError(i18n("unexpected end of document"));
0157         return false;
0158     }
0159 
0160     return true;
0161 }
0162 
0163 /*!
0164  * Read an XML attribute and convert it to int
0165  * \param name attribute name
0166  * \param ok pointer to report back whether the attribute value could be determined (may be NULL)
0167  * \return the attribute value if found and converted, otherwise zero (in this case *ok is false)
0168  */
0169 int XmlStreamReader::readAttributeInt(const QString& name, bool* ok) {
0170     QString str = attributes().value(namespaceUri().toString(), name).toString();
0171     if (str.isEmpty()) {
0172         if (ok)
0173             *ok = false;
0174         return 0;
0175     }
0176 
0177     return str.toInt(ok);
0178 }