File indexing completed on 2024-11-10 04:40:47

0001 /*
0002     SPDX-FileCopyrightText: 2017 Daniel Vrátil <dvratil@kde.og>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "xmlparser.h"
0008 #include "nodetree.h"
0009 
0010 #include <QFile>
0011 
0012 #include <iostream>
0013 
0014 #define qPrintableRef(x) reinterpret_cast<const char *>((x).unicode())
0015 
0016 XmlParser::XmlParser()
0017 {
0018 }
0019 
0020 XmlParser::~XmlParser()
0021 {
0022 }
0023 
0024 Node const *XmlParser::tree() const
0025 {
0026     return mTree.get();
0027 }
0028 
0029 bool XmlParser::parse(const QString &filename)
0030 {
0031     QFile file(filename);
0032     if (!file.open(QIODevice::ReadOnly)) {
0033         std::cerr << qPrintable(file.errorString());
0034         return false;
0035     }
0036 
0037     mReader.setDevice(&file);
0038     while (!mReader.atEnd()) {
0039         mReader.readNext();
0040         if (mReader.isStartElement() && mReader.name() == QLatin1StringView("protocol")) {
0041             if (!parseProtocol()) {
0042                 return false;
0043             }
0044         }
0045     }
0046 
0047     return true;
0048 }
0049 
0050 bool XmlParser::parseProtocol()
0051 {
0052     Q_ASSERT(mReader.name() == QLatin1StringView("protocol"));
0053 
0054     const auto attrs = mReader.attributes();
0055     if (!attrs.hasAttribute(QLatin1StringView("version"))) {
0056         printError(QStringLiteral("Missing \"version\" attribute in <protocol> tag!"));
0057         return false;
0058     }
0059 
0060     auto documentNode = std::make_unique<DocumentNode>(attrs.value(QLatin1StringView("version")).toInt());
0061 
0062     while (!mReader.atEnd() && !(mReader.isEndElement() && mReader.name() == QLatin1StringView("protocol"))) {
0063         mReader.readNext();
0064         if (mReader.isStartElement()) {
0065             const auto elemName = mReader.name();
0066             if (elemName == QLatin1StringView("class") || elemName == QLatin1StringView("command") || elemName == QLatin1StringView("response")
0067                 || elemName == QLatin1StringView("notification")) {
0068                 if (!parseCommand(documentNode.get())) {
0069                     return false;
0070                 }
0071             } else {
0072                 printError(QStringLiteral("Unsupported tag: ").append(mReader.name()));
0073                 return false;
0074             }
0075         }
0076     }
0077 
0078     mTree = std::move(documentNode);
0079 
0080     return true;
0081 }
0082 
0083 bool XmlParser::parseCommand(DocumentNode *documentNode)
0084 {
0085     const auto attrs = mReader.attributes();
0086     if (!attrs.hasAttribute(QLatin1StringView("name"))) {
0087         printError(QStringLiteral("Missing \"name\" attribute  in command tag!"));
0088         return false;
0089     }
0090 
0091     auto classNode = new ClassNode(attrs.value(QLatin1StringView("name")).toString(), ClassNode::elementNameToType(mReader.name()), documentNode);
0092     new CtorNode({}, classNode);
0093 
0094     while (!mReader.atEnd() && !(mReader.isEndElement() && classNode->classType() == ClassNode::elementNameToType(mReader.name()))) {
0095         mReader.readNext();
0096         if (mReader.isStartElement()) {
0097             if (mReader.name() == QLatin1StringView("ctor")) {
0098                 if (!parseCtor(classNode)) {
0099                     return false;
0100                 }
0101             } else if (mReader.name() == QLatin1StringView("enum") || mReader.name() == QLatin1StringView("flag")) {
0102                 if (!parseEnum(classNode)) {
0103                     return false;
0104                 }
0105             } else if (mReader.name() == QLatin1StringView("param")) {
0106                 if (!parseParam(classNode)) {
0107                     return false;
0108                 }
0109             } else {
0110                 printError(QStringLiteral("Unsupported tag: ").append(mReader.name()));
0111                 return false;
0112             }
0113         }
0114     }
0115 
0116     return true;
0117 }
0118 
0119 bool XmlParser::parseCtor(ClassNode *classNode)
0120 {
0121     QList<CtorNode::Argument> args;
0122     while (!mReader.atEnd() && !(mReader.isEndElement() && (mReader.name() == QLatin1StringView("ctor")))) {
0123         mReader.readNext();
0124         if (mReader.isStartElement()) {
0125             if (mReader.name() == QLatin1StringView("arg")) {
0126                 const auto attrs = mReader.attributes();
0127                 const QString name = attrs.value(QLatin1StringView("name")).toString();
0128                 const QString def = attrs.value(QLatin1StringView("default")).toString();
0129                 args << CtorNode::Argument{name, QString(), def};
0130             } else {
0131                 printError(QStringLiteral("Unsupported tag: ").append(mReader.name()));
0132                 return false;
0133             }
0134         }
0135     }
0136     new CtorNode(args, classNode);
0137 
0138     return true;
0139 }
0140 
0141 bool XmlParser::parseEnum(ClassNode *classNode)
0142 {
0143     const auto attrs = mReader.attributes();
0144     if (!attrs.hasAttribute(QLatin1StringView("name"))) {
0145         printError(QStringLiteral("Missing \"name\" attribute in enum/flag tag!"));
0146         return false;
0147     }
0148 
0149     auto enumNode = new EnumNode(attrs.value(QLatin1StringView("name")).toString(), EnumNode::elementNameToType(mReader.name()), classNode);
0150 
0151     while (!mReader.atEnd() && !(mReader.isEndElement() && (enumNode->enumType() == EnumNode::elementNameToType(mReader.name())))) {
0152         mReader.readNext();
0153         if (mReader.isStartElement()) {
0154             if (mReader.name() == QLatin1StringView("value")) {
0155                 if (!parseEnumValue(enumNode)) {
0156                     return false;
0157                 }
0158             } else {
0159                 printError(QStringLiteral("Invalid tag inside of enum/flag tag: ").append(mReader.name()));
0160                 return false;
0161             }
0162         }
0163     }
0164 
0165     return true;
0166 }
0167 
0168 bool XmlParser::parseEnumValue(EnumNode *enumNode)
0169 {
0170     Q_ASSERT(mReader.name() == QLatin1StringView("value"));
0171 
0172     const auto attrs = mReader.attributes();
0173     if (!attrs.hasAttribute(QLatin1StringView("name"))) {
0174         printError(QStringLiteral("Missing \"name\" attribute in <value> tag!"));
0175         return false;
0176     }
0177 
0178     auto valueNode = new EnumValueNode(attrs.value(QLatin1StringView("name")).toString(), enumNode);
0179     if (attrs.hasAttribute(QLatin1StringView("value"))) {
0180         valueNode->setValue(attrs.value(QLatin1StringView("value")).toString());
0181     }
0182 
0183     return true;
0184 }
0185 
0186 bool XmlParser::parseParam(ClassNode *classNode)
0187 {
0188     Q_ASSERT(mReader.name() == QLatin1StringView("param"));
0189 
0190     const auto attrs = mReader.attributes();
0191     if (!attrs.hasAttribute(QLatin1StringView("name"))) {
0192         printError(QStringLiteral("Missing \"name\" attribute in <param> tag!"));
0193         return false;
0194     }
0195     if (!attrs.hasAttribute(QLatin1StringView("type"))) {
0196         printError(QStringLiteral("Missing \"type\" attribute in <param> tag!"));
0197         return false;
0198     }
0199 
0200     const auto name = attrs.value(QLatin1StringView("name")).toString();
0201     const auto type = attrs.value(QLatin1StringView("type")).toString();
0202 
0203     for (const auto child : classNode->children()) {
0204         if (child->type() == Node::Ctor) {
0205             auto ctor = const_cast<CtorNode *>(static_cast<const CtorNode *>(child));
0206             ctor->setArgumentType(name, type);
0207         }
0208     }
0209 
0210     auto paramNode = new PropertyNode(name, type, classNode);
0211 
0212     if (attrs.hasAttribute(QLatin1StringView("default"))) {
0213         paramNode->setDefaultValue(attrs.value(QLatin1StringView("default")).toString());
0214     }
0215     if (attrs.hasAttribute(QLatin1StringView("readOnly"))) {
0216         paramNode->setReadOnly(attrs.value(QLatin1StringView("readOnly")) == QLatin1StringView("true"));
0217     }
0218     if (attrs.hasAttribute(QLatin1StringView("asReference"))) {
0219         paramNode->setAsReference(attrs.value(QLatin1StringView("asReference")) == QLatin1StringView("true"));
0220     }
0221 
0222     while (!mReader.atEnd() && !(mReader.isEndElement() && mReader.name() == QLatin1StringView("param"))) {
0223         mReader.readNext();
0224         if (mReader.isStartElement()) {
0225             if (mReader.name() == QLatin1StringView("setter")) {
0226                 if (!parseSetter(paramNode)) {
0227                     return false;
0228                 }
0229             } else if (mReader.name() == QLatin1StringView("depends")) {
0230                 auto dependsAttrs = mReader.attributes();
0231                 if (!dependsAttrs.hasAttribute(QLatin1StringView("enum"))) {
0232                     printError(QStringLiteral("Missing \"enum\" attribute in <depends> tag!"));
0233                     return false;
0234                 }
0235                 if (!dependsAttrs.hasAttribute(QLatin1StringView("value"))) {
0236                     printError(QStringLiteral("Missing \"value\" attribute in <depends> tag!"));
0237                     return false;
0238                 }
0239                 paramNode->addDependency(dependsAttrs.value(QLatin1StringView("enum")).toString(), dependsAttrs.value(QLatin1StringView("value")).toString());
0240             } else {
0241                 printError(QStringLiteral("Unknown tag: ").append(mReader.name()));
0242                 return false;
0243             }
0244         }
0245     }
0246 
0247     return true;
0248 }
0249 
0250 bool XmlParser::parseSetter(PropertyNode *parent)
0251 {
0252     const auto attrs = mReader.attributes();
0253     auto setter = new PropertyNode::Setter;
0254     setter->name = attrs.value(QLatin1StringView("name")).toString();
0255     setter->type = attrs.value(QLatin1StringView("type")).toString();
0256 
0257     while (!mReader.atEnd() && !(mReader.isEndElement() && mReader.name() == QLatin1StringView("setter"))) {
0258         mReader.readNext();
0259         if (mReader.isStartElement()) {
0260             if (mReader.name() == QLatin1StringView("append")) {
0261                 setter->append = mReader.attributes().value(QLatin1StringView("name")).toString();
0262             } else if (mReader.name() == QLatin1StringView("remove")) {
0263                 setter->remove = mReader.attributes().value(QLatin1StringView("name")).toString();
0264             }
0265         }
0266     }
0267 
0268     parent->setSetter(setter);
0269 
0270     return true;
0271 }
0272 
0273 void XmlParser::printError(const QString &error)
0274 {
0275     std::cerr << "Error:" << mReader.lineNumber() << ":" << mReader.columnNumber() << ": " << qPrintable(error) << std::endl;
0276 }