8#include <QtCore/qmap.h>
9#include <QtCore/qvariant.h>
10#include <QtCore/qtextstream.h>
11#include <QtCore/qdebug.h>
17using namespace Qt::StringLiterals;
21#define qDBusParserWarning(format, ...)
24 m_reporter->warning(m_currentLocation, format "\n", ##__VA_ARGS__);
26 qCDebug(dbusParser, "Warning: " format, ##__VA_ARGS__);
29#define qDBusParserError(format, ...)
32 m_reporter->error(m_currentLocation, format "\n", ##__VA_ARGS__);
34 qCDebug(dbusParser, "Error: " format, ##__VA_ARGS__);
37bool QDBusXmlParser::parseArg(
const QXmlStreamAttributes &attributes,
38 QDBusIntrospection::Argument &argData)
40 Q_ASSERT(m_currentInterface);
42 const QString argType = attributes.value(
"type"_L1).toString();
44 bool ok = QDBusUtil::isValidSingleSignature(argType);
46 qDBusParserError(
"Invalid D-Bus type signature '%s' found while parsing introspection",
50 argData.name = attributes.value(
"name"_L1).toString();
51 argData.type = argType;
53 m_currentInterface->introspection +=
" <arg"_L1;
54 if (attributes.hasAttribute(
"direction"_L1)) {
55 const QString direction = attributes.value(
"direction"_L1).toString();
56 m_currentInterface->introspection +=
" direction=\""_L1 + direction + u'"';
58 m_currentInterface->introspection +=
" type=\""_L1 + argData.type + u'"';
59 if (!argData.name.isEmpty())
60 m_currentInterface->introspection +=
" name=\""_L1 + argData.name + u'"';
61 m_currentInterface->introspection +=
"/>\n"_L1;
66bool QDBusXmlParser::parseAnnotation(QDBusIntrospection::Annotations &annotations,
67 bool interfaceAnnotation)
69 Q_ASSERT(m_currentInterface);
70 Q_ASSERT(m_xml.isStartElement() && m_xml.name() ==
"annotation"_L1);
72 QDBusIntrospection::Annotation annotation;
73 annotation.location = m_currentLocation;
75 const QXmlStreamAttributes attributes = m_xml.attributes();
76 annotation.name = attributes.value(
"name"_L1).toString();
78 if (!QDBusUtil::isValidInterfaceName(annotation.name)) {
79 qDBusParserError(
"Invalid D-Bus annotation '%s' found while parsing introspection",
80 qPrintable(annotation.name));
83 annotation.value = attributes.value(
"value"_L1).toString();
84 annotations.insert(annotation.name, annotation);
85 if (!interfaceAnnotation)
86 m_currentInterface->introspection +=
" "_L1;
87 m_currentInterface->introspection +=
" <annotation value=\""_L1
88 + annotation.value.toHtmlEscaped() +
"\" name=\""_L1 + annotation.name +
"\"/>\n"_L1;
92bool QDBusXmlParser::parseProperty(QDBusIntrospection::Property &propertyData)
94 Q_ASSERT(m_currentInterface);
95 Q_ASSERT(m_xml.isStartElement() && m_xml.name() ==
"property"_L1);
97 QXmlStreamAttributes attributes = m_xml.attributes();
98 const QString propertyName = attributes.value(
"name"_L1).toString();
99 if (!QDBusUtil::isValidMemberName(propertyName)) {
100 qDBusParserWarning(
"Invalid D-Bus member name '%s' found in interface '%s' while parsing "
102 qPrintable(propertyName), qPrintable(m_currentInterface->name));
103 m_xml.skipCurrentElement();
108 propertyData.name = propertyName;
109 propertyData.type = attributes.value(
"type"_L1).toString();
111 if (!QDBusUtil::isValidSingleSignature(propertyData.type)) {
113 qDBusParserError(
"Invalid D-Bus type signature '%s' found in property '%s.%s' while "
114 "parsing introspection",
115 qPrintable(propertyData.type), qPrintable(m_currentInterface->name),
116 qPrintable(propertyName));
119 const QString access = attributes.value(
"access"_L1).toString();
120 if (access ==
"read"_L1)
121 propertyData.access = QDBusIntrospection::Property::Read;
122 else if (access ==
"write"_L1)
123 propertyData.access = QDBusIntrospection::Property::Write;
124 else if (access ==
"readwrite"_L1)
125 propertyData.access = QDBusIntrospection::Property::ReadWrite;
127 qDBusParserError(
"Invalid D-Bus property access '%s' found in property '%s.%s' while "
128 "parsing introspection",
129 qPrintable(access), qPrintable(m_currentInterface->name),
130 qPrintable(propertyName));
134 m_currentInterface->introspection +=
" <property access=\""_L1 + access +
"\" type=\""_L1 + propertyData.type +
"\" name=\""_L1 + propertyName + u'"';
136 if (!readNextStartElement()) {
137 m_currentInterface->introspection +=
"/>\n"_L1;
139 m_currentInterface->introspection +=
">\n"_L1;
142 if (m_xml.name() ==
"annotation"_L1) {
143 parseAnnotation(propertyData.annotations);
144 }
else if (m_xml.prefix().isEmpty()) {
146 qPrintable(m_xml.name().toString()));
148 m_xml.skipCurrentElement();
149 }
while (readNextStartElement());
151 m_currentInterface->introspection +=
" </property>\n"_L1;
154 if (!m_xml.isEndElement() || m_xml.name() !=
"property"_L1) {
155 qDBusParserError(
"Invalid property specification: '%s'", qPrintable(m_xml.tokenString()));
162bool QDBusXmlParser::parseMethod(QDBusIntrospection::Method &methodData)
164 Q_ASSERT(m_currentInterface);
165 Q_ASSERT(m_xml.isStartElement() && m_xml.name() ==
"method"_L1);
167 const QXmlStreamAttributes attributes = m_xml.attributes();
168 const QString methodName = attributes.value(
"name"_L1).toString();
169 if (!QDBusUtil::isValidMemberName(methodName)) {
170 qDBusParserError(
"Invalid D-Bus member name '%s' found in interface '%s' while parsing "
172 qPrintable(methodName), qPrintable(m_currentInterface->name));
176 methodData.name = methodName;
177 m_currentInterface->introspection +=
" <method name=\""_L1 + methodName + u'"';
179 QDBusIntrospection::Arguments outArguments;
180 QDBusIntrospection::Arguments inArguments;
181 QDBusIntrospection::Annotations annotations;
183 if (!readNextStartElement()) {
184 m_currentInterface->introspection +=
"/>\n"_L1;
186 m_currentInterface->introspection +=
">\n"_L1;
189 if (m_xml.name() ==
"annotation"_L1) {
190 parseAnnotation(annotations);
191 }
else if (m_xml.name() ==
"arg"_L1) {
192 const QXmlStreamAttributes attributes = m_xml.attributes();
193 const QString direction = attributes.value(
"direction"_L1).toString();
194 QDBusIntrospection::Argument argument;
195 argument.location = m_currentLocation;
196 if (!attributes.hasAttribute(
"direction"_L1) || direction ==
"in"_L1) {
197 parseArg(attributes, argument);
198 inArguments << argument;
199 }
else if (direction ==
"out"_L1) {
200 parseArg(attributes, argument);
201 outArguments << argument;
203 }
else if (m_xml.prefix().isEmpty()) {
205 qPrintable(m_xml.name().toString()));
207 m_xml.skipCurrentElement();
208 }
while (readNextStartElement());
210 m_currentInterface->introspection +=
" </method>\n"_L1;
213 methodData.inputArgs = inArguments;
214 methodData.outputArgs = outArguments;
215 methodData.annotations = annotations;
220bool QDBusXmlParser::parseSignal(QDBusIntrospection::Signal &signalData)
222 Q_ASSERT(m_currentInterface);
223 Q_ASSERT(m_xml.isStartElement() && m_xml.name() ==
"signal"_L1);
225 const QXmlStreamAttributes attributes = m_xml.attributes();
226 const QString signalName = attributes.value(
"name"_L1).toString();
228 if (!QDBusUtil::isValidMemberName(signalName)) {
229 qDBusParserError(
"Invalid D-Bus member name '%s' found in interface '%s' while parsing "
231 qPrintable(signalName), qPrintable(m_currentInterface->name));
235 signalData.name = signalName;
236 m_currentInterface->introspection +=
" <signal name=\""_L1 + signalName + u'"';
238 QDBusIntrospection::Arguments arguments;
239 QDBusIntrospection::Annotations annotations;
241 if (!readNextStartElement()) {
242 m_currentInterface->introspection +=
"/>\n"_L1;
244 m_currentInterface->introspection +=
">\n"_L1;
247 if (m_xml.name() ==
"annotation"_L1) {
248 parseAnnotation(annotations);
249 }
else if (m_xml.name() ==
"arg"_L1) {
250 const QXmlStreamAttributes attributes = m_xml.attributes();
251 QDBusIntrospection::Argument argument;
252 argument.location = m_currentLocation;
253 if (!attributes.hasAttribute(
"direction"_L1) ||
254 attributes.value(
"direction"_L1) ==
"out"_L1) {
255 parseArg(attributes, argument);
256 arguments << argument;
260 qPrintable(m_xml.name().toString()));
262 m_xml.skipCurrentElement();
263 }
while (readNextStartElement());
265 m_currentInterface->introspection +=
" </signal>\n"_L1;
268 signalData.outputArgs = arguments;
269 signalData.annotations = annotations;
274void QDBusXmlParser::readInterface()
276 Q_ASSERT(!m_currentInterface);
278 const QString ifaceName = m_xml.attributes().value(
"name"_L1).toString();
279 if (!QDBusUtil::isValidInterfaceName(ifaceName)) {
280 qDBusParserError(
"Invalid D-Bus interface name '%s' found while parsing introspection",
281 qPrintable(ifaceName));
285 m_object->interfaces.append(ifaceName);
287 m_currentInterface = std::make_unique<QDBusIntrospection::Interface>();
288 m_currentInterface->location = m_currentLocation;
289 m_currentInterface->name = ifaceName;
290 m_currentInterface->introspection +=
" <interface name=\""_L1 + ifaceName +
"\">\n"_L1;
292 while (readNextStartElement()) {
293 if (m_xml.name() ==
"method"_L1) {
294 QDBusIntrospection::Method methodData;
295 methodData.location = m_currentLocation;
296 if (parseMethod(methodData))
297 m_currentInterface->methods.insert(methodData.name, methodData);
298 }
else if (m_xml.name() ==
"signal"_L1) {
299 QDBusIntrospection::Signal signalData;
300 signalData.location = m_currentLocation;
301 if (parseSignal(signalData))
302 m_currentInterface->signals_.insert(signalData.name, signalData);
303 }
else if (m_xml.name() ==
"property"_L1) {
304 QDBusIntrospection::Property propertyData;
305 propertyData.location = m_currentLocation;
306 if (parseProperty(propertyData))
307 m_currentInterface->properties.insert(propertyData.name, propertyData);
308 }
else if (m_xml.name() ==
"annotation"_L1) {
309 parseAnnotation(m_currentInterface->annotations,
true);
310 m_xml.skipCurrentElement();
312 if (m_xml.prefix().isEmpty()) {
314 qPrintable(m_xml.name().toString()));
316 m_xml.skipCurrentElement();
320 m_currentInterface->introspection +=
" </interface>"_L1;
324 QSharedDataPointer<QDBusIntrospection::Interface>(m_currentInterface.release()));
326 if (!m_xml.isEndElement() || m_xml.name() !=
"interface"_L1) {
331void QDBusXmlParser::readNode(
int nodeLevel)
333 const QString objName = m_xml.attributes().value(
"name"_L1).toString();
334 QString fullName = m_object->path;
335 if (!(fullName.endsWith(u'/') || (nodeLevel == 0 && objName.startsWith(u'/'))))
336 fullName.append(u'/');
339 if (!QDBusUtil::isValidObjectPath(fullName)) {
340 qDBusParserError(
"Invalid D-Bus object path '%s' found while parsing introspection",
341 qPrintable(fullName));
346 m_object->childObjects.append(objName);
348 m_object->location = m_currentLocation;
351void QDBusXmlParser::updateCurrentLocation()
354 QDBusIntrospection::SourceLocation{ m_xml.lineNumber(), m_xml.columnNumber() };
359bool QDBusXmlParser::readNextStartElement()
361 updateCurrentLocation();
363 while (m_xml.readNext() != QXmlStreamReader::Invalid) {
364 if (m_xml.isEndElement())
366 else if (m_xml.isStartElement())
368 updateCurrentLocation();
373QDBusXmlParser::QDBusXmlParser(
const QString &service,
const QString &path,
const QString &xmlData,
374 QDBusIntrospection::DiagnosticsReporter *reporter)
375 : m_service(service),
377 m_object(
new QDBusIntrospection::Object),
381 m_object->service = m_service;
382 m_object->path = m_path;
386 while (!m_xml.atEnd()) {
387 updateCurrentLocation();
390 switch (m_xml.tokenType()) {
391 case QXmlStreamReader::StartElement:
392 if (m_xml.name() ==
"node"_L1) {
393 readNode(++nodeLevel);
394 }
else if (m_xml.name() ==
"interface"_L1) {
397 if (m_xml.prefix().isEmpty()) {
399 qPrintable(m_xml.name().toString()));
401 m_xml.skipCurrentElement();
404 case QXmlStreamReader::EndElement:
405 if (m_xml.name() ==
"node"_L1) {
409 qPrintable(m_xml.name().toString()));
412 case QXmlStreamReader::StartDocument:
413 case QXmlStreamReader::EndDocument:
414 case QXmlStreamReader::DTD:
417 case QXmlStreamReader::Comment:
420 case QXmlStreamReader::Characters:
422 if (m_xml.isWhitespace())
431 if (m_xml.hasError())
#define qDBusParserError(format,...)
#define qDBusParserWarning(format,...)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")