diff options
Diffstat (limited to 'src/dbus/qdbusxmlparser.cpp')
-rw-r--r-- | src/dbus/qdbusxmlparser.cpp | 367 |
1 files changed, 216 insertions, 151 deletions
diff --git a/src/dbus/qdbusxmlparser.cpp b/src/dbus/qdbusxmlparser.cpp index 509ca6e77c..c2e8df8be7 100644 --- a/src/dbus/qdbusxmlparser.cpp +++ b/src/dbus/qdbusxmlparser.cpp @@ -7,7 +7,6 @@ #include <QtCore/qmap.h> #include <QtCore/qvariant.h> #include <QtCore/qtextstream.h> -#include <QtCore/qxmlstream.h> #include <QtCore/qdebug.h> #ifndef QT_NO_DBUS @@ -18,67 +17,89 @@ using namespace Qt::StringLiterals; Q_LOGGING_CATEGORY(dbusParser, "dbus.parser", QtWarningMsg) -#define qDBusParserError(...) qCDebug(dbusParser, ##__VA_ARGS__) - -static bool parseArg(const QXmlStreamAttributes &attributes, QDBusIntrospection::Argument &argData, - QDBusIntrospection::Interface *ifaceData) +#define qDBusParserWarning(format, ...) \ + do { \ + if (m_reporter) \ + m_reporter->warning(m_currentLocation, format "\n", ##__VA_ARGS__); \ + else \ + qCDebug(dbusParser, "Warning: " format, ##__VA_ARGS__); \ + } while (0) + +#define qDBusParserError(format, ...) \ + do { \ + if (m_reporter) \ + m_reporter->error(m_currentLocation, format "\n", ##__VA_ARGS__); \ + else \ + qCDebug(dbusParser, "Error: " format, ##__VA_ARGS__); \ + } while (0) + +bool QDBusXmlParser::parseArg(const QXmlStreamAttributes &attributes, + QDBusIntrospection::Argument &argData) { + Q_ASSERT(m_currentInterface); + const QString argType = attributes.value("type"_L1).toString(); bool ok = QDBusUtil::isValidSingleSignature(argType); if (!ok) { - qDBusParserError("Invalid D-BUS type signature '%s' found while parsing introspection", - qPrintable(argType)); + qDBusParserError("Invalid D-Bus type signature '%s' found while parsing introspection", + qPrintable(argType)); } argData.name = attributes.value("name"_L1).toString(); argData.type = argType; - ifaceData->introspection += " <arg"_L1; + m_currentInterface->introspection += " <arg"_L1; if (attributes.hasAttribute("direction"_L1)) { const QString direction = attributes.value("direction"_L1).toString(); - ifaceData->introspection += " direction=\""_L1 + direction + u'"'; + m_currentInterface->introspection += " direction=\""_L1 + direction + u'"'; } - ifaceData->introspection += " type=\""_L1 + argData.type + u'"'; + m_currentInterface->introspection += " type=\""_L1 + argData.type + u'"'; if (!argData.name.isEmpty()) - ifaceData->introspection += " name=\""_L1 + argData.name + u'"'; - ifaceData->introspection += "/>\n"_L1; + m_currentInterface->introspection += " name=\""_L1 + argData.name + u'"'; + m_currentInterface->introspection += "/>\n"_L1; return ok; } -static bool parseAnnotation(const QXmlStreamReader &xml, QDBusIntrospection::Annotations &annotations, - QDBusIntrospection::Interface *ifaceData, bool interfaceAnnotation = false) +bool QDBusXmlParser::parseAnnotation(QDBusIntrospection::Annotations &annotations, + bool interfaceAnnotation) { - Q_ASSERT(xml.isStartElement() && xml.name() == "annotation"_L1); + Q_ASSERT(m_currentInterface); + Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "annotation"_L1); - const QXmlStreamAttributes attributes = xml.attributes(); - const QString name = attributes.value("name"_L1).toString(); + QDBusIntrospection::Annotation annotation; + annotation.location = m_currentLocation; - if (!QDBusUtil::isValidInterfaceName(name)) { - qDBusParserError("Invalid D-BUS annotation '%s' found while parsing introspection", - qPrintable(name)); + const QXmlStreamAttributes attributes = m_xml.attributes(); + annotation.name = attributes.value("name"_L1).toString(); + + if (!QDBusUtil::isValidInterfaceName(annotation.name)) { + qDBusParserError("Invalid D-Bus annotation '%s' found while parsing introspection", + qPrintable(annotation.name)); return false; } - const QString value = attributes.value("value"_L1).toString(); - annotations.insert(name, value); + annotation.value = attributes.value("value"_L1).toString(); + annotations.insert(annotation.name, annotation); if (!interfaceAnnotation) - ifaceData->introspection += " "_L1; - ifaceData->introspection += " <annotation value=\""_L1 + value.toHtmlEscaped() + "\" name=\""_L1 + name + "\"/>\n"_L1; + m_currentInterface->introspection += " "_L1; + m_currentInterface->introspection += " <annotation value=\""_L1 + + annotation.value.toHtmlEscaped() + "\" name=\""_L1 + annotation.name + "\"/>\n"_L1; return true; } -static bool parseProperty(QXmlStreamReader &xml, QDBusIntrospection::Property &propertyData, - QDBusIntrospection::Interface *ifaceData) +bool QDBusXmlParser::parseProperty(QDBusIntrospection::Property &propertyData) { - Q_ASSERT(xml.isStartElement() && xml.name() == "property"_L1); + Q_ASSERT(m_currentInterface); + Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "property"_L1); - QXmlStreamAttributes attributes = xml.attributes(); + QXmlStreamAttributes attributes = m_xml.attributes(); const QString propertyName = attributes.value("name"_L1).toString(); if (!QDBusUtil::isValidMemberName(propertyName)) { - qDBusParserError("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection", - qPrintable(propertyName), qPrintable(ifaceData->name)); - xml.skipCurrentElement(); + qDBusParserWarning("Invalid D-Bus member name '%s' found in interface '%s' while parsing " + "introspection", + qPrintable(propertyName), qPrintable(m_currentInterface->name)); + m_xml.skipCurrentElement(); return false; } @@ -88,9 +109,10 @@ static bool parseProperty(QXmlStreamReader &xml, QDBusIntrospection::Property &p if (!QDBusUtil::isValidSingleSignature(propertyData.type)) { // cannot be! - qDBusParserError("Invalid D-BUS type signature '%s' found in property '%s.%s' while parsing introspection", - qPrintable(propertyData.type), qPrintable(ifaceData->name), - qPrintable(propertyName)); + qDBusParserError("Invalid D-Bus type signature '%s' found in property '%s.%s' while " + "parsing introspection", + qPrintable(propertyData.type), qPrintable(m_currentInterface->name), + qPrintable(propertyName)); } const QString access = attributes.value("access"_L1).toString(); @@ -101,85 +123,90 @@ static bool parseProperty(QXmlStreamReader &xml, QDBusIntrospection::Property &p else if (access == "readwrite"_L1) propertyData.access = QDBusIntrospection::Property::ReadWrite; else { - qDBusParserError("Invalid D-BUS property access '%s' found in property '%s.%s' while parsing introspection", - qPrintable(access), qPrintable(ifaceData->name), - qPrintable(propertyName)); + qDBusParserError("Invalid D-Bus property access '%s' found in property '%s.%s' while " + "parsing introspection", + qPrintable(access), qPrintable(m_currentInterface->name), + qPrintable(propertyName)); return false; // invalid one! } - ifaceData->introspection += " <property access=\""_L1 + access + "\" type=\""_L1 + propertyData.type + "\" name=\""_L1 + propertyName + u'"'; + m_currentInterface->introspection += " <property access=\""_L1 + access + "\" type=\""_L1 + propertyData.type + "\" name=\""_L1 + propertyName + u'"'; - if (!xml.readNextStartElement()) { - ifaceData->introspection += "/>\n"_L1; + if (!readNextStartElement()) { + m_currentInterface->introspection += "/>\n"_L1; } else { - ifaceData->introspection += ">\n"_L1; + m_currentInterface->introspection += ">\n"_L1; do { - if (xml.name() == "annotation"_L1) { - parseAnnotation(xml, propertyData.annotations, ifaceData); - } else if (xml.prefix().isEmpty()) { - qDBusParserError() << "Unknown element" << xml.name() << "while checking for annotations"; + if (m_xml.name() == "annotation"_L1) { + parseAnnotation(propertyData.annotations); + } else if (m_xml.prefix().isEmpty()) { + qDBusParserWarning("Unknown element '%s' while checking for annotations", + qPrintable(m_xml.name().toString())); } - xml.skipCurrentElement(); - } while (xml.readNextStartElement()); + m_xml.skipCurrentElement(); + } while (readNextStartElement()); - ifaceData->introspection += " </property>\n"_L1; + m_currentInterface->introspection += " </property>\n"_L1; } - if (!xml.isEndElement() || xml.name() != "property"_L1) { - qDBusParserError() << "Invalid property specification" << xml.tokenString() << xml.name(); + if (!m_xml.isEndElement() || m_xml.name() != "property"_L1) { + qDBusParserError("Invalid property specification: '%s'", qPrintable(m_xml.tokenString())); return false; } return true; } -static bool parseMethod(QXmlStreamReader &xml, QDBusIntrospection::Method &methodData, - QDBusIntrospection::Interface *ifaceData) +bool QDBusXmlParser::parseMethod(QDBusIntrospection::Method &methodData) { - Q_ASSERT(xml.isStartElement() && xml.name() == "method"_L1); + Q_ASSERT(m_currentInterface); + Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "method"_L1); - const QXmlStreamAttributes attributes = xml.attributes(); + const QXmlStreamAttributes attributes = m_xml.attributes(); const QString methodName = attributes.value("name"_L1).toString(); if (!QDBusUtil::isValidMemberName(methodName)) { - qDBusParserError("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection", - qPrintable(methodName), qPrintable(ifaceData->name)); + qDBusParserError("Invalid D-Bus member name '%s' found in interface '%s' while parsing " + "introspection", + qPrintable(methodName), qPrintable(m_currentInterface->name)); return false; } methodData.name = methodName; - ifaceData->introspection += " <method name=\""_L1 + methodName + u'"'; + m_currentInterface->introspection += " <method name=\""_L1 + methodName + u'"'; QDBusIntrospection::Arguments outArguments; QDBusIntrospection::Arguments inArguments; QDBusIntrospection::Annotations annotations; - if (!xml.readNextStartElement()) { - ifaceData->introspection += "/>\n"_L1; + if (!readNextStartElement()) { + m_currentInterface->introspection += "/>\n"_L1; } else { - ifaceData->introspection += ">\n"_L1; + m_currentInterface->introspection += ">\n"_L1; do { - if (xml.name() == "annotation"_L1) { - parseAnnotation(xml, annotations, ifaceData); - } else if (xml.name() == "arg"_L1) { - const QXmlStreamAttributes attributes = xml.attributes(); + if (m_xml.name() == "annotation"_L1) { + parseAnnotation(annotations); + } else if (m_xml.name() == "arg"_L1) { + const QXmlStreamAttributes attributes = m_xml.attributes(); const QString direction = attributes.value("direction"_L1).toString(); QDBusIntrospection::Argument argument; + argument.location = m_currentLocation; if (!attributes.hasAttribute("direction"_L1) || direction == "in"_L1) { - parseArg(attributes, argument, ifaceData); + parseArg(attributes, argument); inArguments << argument; } else if (direction == "out"_L1) { - parseArg(attributes, argument, ifaceData); + parseArg(attributes, argument); outArguments << argument; } - } else if (xml.prefix().isEmpty()) { - qDBusParserError() << "Unknown element" << xml.name() << "while checking for method arguments"; + } else if (m_xml.prefix().isEmpty()) { + qDBusParserWarning("Unknown element '%s' while checking for method arguments", + qPrintable(m_xml.name().toString())); } - xml.skipCurrentElement(); - } while (xml.readNextStartElement()); + m_xml.skipCurrentElement(); + } while (readNextStartElement()); - ifaceData->introspection += " </method>\n"_L1; + m_currentInterface->introspection += " </method>\n"_L1; } methodData.inputArgs = inArguments; @@ -189,50 +216,52 @@ static bool parseMethod(QXmlStreamReader &xml, QDBusIntrospection::Method &metho return true; } - -static bool parseSignal(QXmlStreamReader &xml, QDBusIntrospection::Signal &signalData, - QDBusIntrospection::Interface *ifaceData) +bool QDBusXmlParser::parseSignal(QDBusIntrospection::Signal &signalData) { - Q_ASSERT(xml.isStartElement() && xml.name() == "signal"_L1); + Q_ASSERT(m_currentInterface); + Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "signal"_L1); - const QXmlStreamAttributes attributes = xml.attributes(); + const QXmlStreamAttributes attributes = m_xml.attributes(); const QString signalName = attributes.value("name"_L1).toString(); if (!QDBusUtil::isValidMemberName(signalName)) { - qDBusParserError("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection", - qPrintable(signalName), qPrintable(ifaceData->name)); + qDBusParserError("Invalid D-Bus member name '%s' found in interface '%s' while parsing " + "introspection", + qPrintable(signalName), qPrintable(m_currentInterface->name)); return false; } signalData.name = signalName; - ifaceData->introspection += " <signal name=\""_L1 + signalName + u'"'; + m_currentInterface->introspection += " <signal name=\""_L1 + signalName + u'"'; QDBusIntrospection::Arguments arguments; QDBusIntrospection::Annotations annotations; - if (!xml.readNextStartElement()) { - ifaceData->introspection += "/>\n"_L1; + if (!readNextStartElement()) { + m_currentInterface->introspection += "/>\n"_L1; } else { - ifaceData->introspection += ">\n"_L1; + m_currentInterface->introspection += ">\n"_L1; do { - if (xml.name() == "annotation"_L1) { - parseAnnotation(xml, annotations, ifaceData); - } else if (xml.name() == "arg"_L1) { - const QXmlStreamAttributes attributes = xml.attributes(); + if (m_xml.name() == "annotation"_L1) { + parseAnnotation(annotations); + } else if (m_xml.name() == "arg"_L1) { + const QXmlStreamAttributes attributes = m_xml.attributes(); QDBusIntrospection::Argument argument; + argument.location = m_currentLocation; if (!attributes.hasAttribute("direction"_L1) || attributes.value("direction"_L1) == "out"_L1) { - parseArg(attributes, argument, ifaceData); + parseArg(attributes, argument); arguments << argument; } } else { - qDBusParserError() << "Unknown element" << xml.name() << "while checking for signal arguments"; + qDBusParserWarning("Unknown element '%s' while checking for signal arguments", + qPrintable(m_xml.name().toString())); } - xml.skipCurrentElement(); - } while (xml.readNextStartElement()); + m_xml.skipCurrentElement(); + } while (readNextStartElement()); - ifaceData->introspection += " </signal>\n"_L1; + m_currentInterface->introspection += " </signal>\n"_L1; } signalData.outputArgs = arguments; @@ -241,105 +270,142 @@ static bool parseSignal(QXmlStreamReader &xml, QDBusIntrospection::Signal &signa return true; } -static void readInterface(QXmlStreamReader &xml, QDBusIntrospection::Object *objData, - QDBusIntrospection::Interfaces *interfaces) +void QDBusXmlParser::readInterface() { - const QString ifaceName = xml.attributes().value("name"_L1).toString(); + Q_ASSERT(!m_currentInterface); + + const QString ifaceName = m_xml.attributes().value("name"_L1).toString(); if (!QDBusUtil::isValidInterfaceName(ifaceName)) { - qDBusParserError("Invalid D-BUS interface name '%s' found while parsing introspection", - qPrintable(ifaceName)); + qDBusParserError("Invalid D-Bus interface name '%s' found while parsing introspection", + qPrintable(ifaceName)); return; } - objData->interfaces.append(ifaceName); + m_object->interfaces.append(ifaceName); - QDBusIntrospection::Interface *ifaceData = new QDBusIntrospection::Interface; - ifaceData->name = ifaceName; - ifaceData->introspection += " <interface name=\""_L1 + ifaceName + "\">\n"_L1; + m_currentInterface = std::make_unique<QDBusIntrospection::Interface>(); + m_currentInterface->location = m_currentLocation; + m_currentInterface->name = ifaceName; + m_currentInterface->introspection += " <interface name=\""_L1 + ifaceName + "\">\n"_L1; - while (xml.readNextStartElement()) { - if (xml.name() == "method"_L1) { + while (readNextStartElement()) { + if (m_xml.name() == "method"_L1) { QDBusIntrospection::Method methodData; - if (parseMethod(xml, methodData, ifaceData)) - ifaceData->methods.insert(methodData.name, methodData); - } else if (xml.name() == "signal"_L1) { + methodData.location = m_currentLocation; + if (parseMethod(methodData)) + m_currentInterface->methods.insert(methodData.name, methodData); + } else if (m_xml.name() == "signal"_L1) { QDBusIntrospection::Signal signalData; - if (parseSignal(xml, signalData, ifaceData)) - ifaceData->signals_.insert(signalData.name, signalData); - } else if (xml.name() == "property"_L1) { + signalData.location = m_currentLocation; + if (parseSignal(signalData)) + m_currentInterface->signals_.insert(signalData.name, signalData); + } else if (m_xml.name() == "property"_L1) { QDBusIntrospection::Property propertyData; - if (parseProperty(xml, propertyData, ifaceData)) - ifaceData->properties.insert(propertyData.name, propertyData); - } else if (xml.name() == "annotation"_L1) { - parseAnnotation(xml, ifaceData->annotations, ifaceData, true); - xml.skipCurrentElement(); // skip over annotation object + propertyData.location = m_currentLocation; + if (parseProperty(propertyData)) + m_currentInterface->properties.insert(propertyData.name, propertyData); + } else if (m_xml.name() == "annotation"_L1) { + parseAnnotation(m_currentInterface->annotations, true); + m_xml.skipCurrentElement(); // skip over annotation object } else { - if (xml.prefix().isEmpty()) { - qDBusParserError() << "Unknown element while parsing interface" << xml.name(); + if (m_xml.prefix().isEmpty()) { + qDBusParserWarning("Unknown element '%s' while parsing interface", + qPrintable(m_xml.name().toString())); } - xml.skipCurrentElement(); + m_xml.skipCurrentElement(); } } - ifaceData->introspection += " </interface>"_L1; + m_currentInterface->introspection += " </interface>"_L1; - interfaces->insert(ifaceName, QSharedDataPointer<QDBusIntrospection::Interface>(ifaceData)); + m_interfaces.insert( + ifaceName, + QSharedDataPointer<QDBusIntrospection::Interface>(m_currentInterface.release())); - if (!xml.isEndElement() || xml.name() != "interface"_L1) { - qDBusParserError() << "Invalid Interface specification"; + if (!m_xml.isEndElement() || m_xml.name() != "interface"_L1) { + qDBusParserError("Invalid Interface specification"); } } -static void readNode(const QXmlStreamReader &xml, QDBusIntrospection::Object *objData, int nodeLevel) +void QDBusXmlParser::readNode(int nodeLevel) { - const QString objName = xml.attributes().value("name"_L1).toString(); - const QString fullName = objData->path.endsWith(u'/') - ? (objData->path + objName) - : QString(objData->path + u'/' + objName); + const QString objName = m_xml.attributes().value("name"_L1).toString(); + QString fullName = m_object->path; + if (!(fullName.endsWith(u'/') || (nodeLevel == 0 && objName.startsWith(u'/')))) + fullName.append(u'/'); + fullName += objName; + if (!QDBusUtil::isValidObjectPath(fullName)) { - qDBusParserError("Invalid D-BUS object path '%s' found while parsing introspection", - qPrintable(fullName)); + qDBusParserError("Invalid D-Bus object path '%s' found while parsing introspection", + qPrintable(fullName)); return; } if (nodeLevel > 0) - objData->childObjects.append(objName); + m_object->childObjects.append(objName); + else + m_object->location = m_currentLocation; +} + +void QDBusXmlParser::updateCurrentLocation() +{ + m_currentLocation = + QDBusIntrospection::SourceLocation{ m_xml.lineNumber(), m_xml.columnNumber() }; } -QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path, - const QString& xmlData) - : m_service(service), m_path(path), m_object(new QDBusIntrospection::Object) +// Similar to m_xml.readNextElement() but sets current location to point +// to the start element. +bool QDBusXmlParser::readNextStartElement() { -// qDBusParserError() << "parsing" << xmlData; + updateCurrentLocation(); + + while (m_xml.readNext() != QXmlStreamReader::Invalid) { + if (m_xml.isEndElement()) + return false; + else if (m_xml.isStartElement()) + return true; + updateCurrentLocation(); + } + return false; +} +QDBusXmlParser::QDBusXmlParser(const QString &service, const QString &path, const QString &xmlData, + QDBusIntrospection::DiagnosticsReporter *reporter) + : m_service(service), + m_path(path), + m_object(new QDBusIntrospection::Object), + m_xml(xmlData), + m_reporter(reporter) +{ m_object->service = m_service; m_object->path = m_path; - QXmlStreamReader xml(xmlData); - int nodeLevel = -1; - while (!xml.atEnd()) { - xml.readNext(); + while (!m_xml.atEnd()) { + updateCurrentLocation(); + m_xml.readNext(); - switch (xml.tokenType()) { + switch (m_xml.tokenType()) { case QXmlStreamReader::StartElement: - if (xml.name() == "node"_L1) { - readNode(xml, m_object, ++nodeLevel); - } else if (xml.name() == "interface"_L1) { - readInterface(xml, m_object, &m_interfaces); + if (m_xml.name() == "node"_L1) { + readNode(++nodeLevel); + } else if (m_xml.name() == "interface"_L1) { + readInterface(); } else { - if (xml.prefix().isEmpty()) { - qDBusParserError() << "skipping unknown element" << xml.name(); + if (m_xml.prefix().isEmpty()) { + qDBusParserWarning("Skipping unknown element '%s'", + qPrintable(m_xml.name().toString())); } - xml.skipCurrentElement(); + m_xml.skipCurrentElement(); } break; case QXmlStreamReader::EndElement: - if (xml.name() == "node"_L1) { + if (m_xml.name() == "node"_L1) { --nodeLevel; } else { - qDBusParserError() << "Invalid Node declaration" << xml.name(); + qDBusParserError("Invalid node declaration '%s'", + qPrintable(m_xml.name().toString())); } break; case QXmlStreamReader::StartDocument: @@ -352,18 +418,17 @@ QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path, break; case QXmlStreamReader::Characters: // ignore whitespace - if (xml.isWhitespace()) + if (m_xml.isWhitespace()) break; Q_FALLTHROUGH(); default: - qDBusParserError() << "unknown token" << xml.name() << xml.tokenString(); + qDBusParserError("Unknown token: '%s'", qPrintable(m_xml.tokenString())); break; } } - if (xml.hasError()) { - qDBusParserError() << "xml error" << xml.errorString() << "doc" << xmlData; - } + if (m_xml.hasError()) + qDBusParserError("XML error: %s", qPrintable(m_xml.errorString())); } QT_END_NAMESPACE |