1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDBus module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qdbusxmlparser_p.h"
43 #include "qdbusutil_p.h"
45 #include <QtCore/qmap.h>
46 #include <QtCore/qvariant.h>
47 #include <QtCore/qtextstream.h>
48 #include <QtCore/qxmlstream.h>
49 #include <QtCore/qdebug.h>
53 //#define QDBUS_PARSER_DEBUG
54 #ifdef QDBUS_PARSER_DEBUG
55 # define qDBusParserError qDebug
57 # define qDBusParserError if (true) {} else qDebug
62 static bool parseArg(const QXmlStreamAttributes &attributes, QDBusIntrospection::Argument &argData)
64 const QString argType = attributes.value(QLatin1String("type")).toString();
66 bool ok = QDBusUtil::isValidSingleSignature(argType);
68 qDBusParserError("Invalid D-BUS type signature '%s' found while parsing introspection",
72 argData.name = attributes.value(QLatin1String("name")).toString();
73 argData.type = argType;
78 static bool parseAnnotation(const QXmlStreamReader &xml, QDBusIntrospection::Annotations &annotations)
80 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("annotation"));
82 const QXmlStreamAttributes attributes = xml.attributes();
83 const QString name = attributes.value(QLatin1String("name")).toString();
85 if (!QDBusUtil::isValidInterfaceName(name)) {
86 qDBusParserError("Invalid D-BUS annotation '%s' found while parsing introspection",
90 annotations.insert(name, attributes.value(QLatin1String("value")).toString());
94 static bool parseProperty(QXmlStreamReader &xml, QDBusIntrospection::Property &propertyData,
95 const QString &ifaceName)
97 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("property"));
99 QXmlStreamAttributes attributes = xml.attributes();
100 const QString propertyName = attributes.value(QLatin1String("name")).toString();
101 if (!QDBusUtil::isValidMemberName(propertyName)) {
102 qDBusParserError("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection",
103 qPrintable(propertyName), qPrintable(ifaceName));
104 xml.skipCurrentElement();
109 propertyData.name = propertyName;
110 propertyData.type = attributes.value(QLatin1String("type")).toString();
112 if (!QDBusUtil::isValidSingleSignature(propertyData.type)) {
114 qDBusParserError("Invalid D-BUS type signature '%s' found in property '%s.%s' while parsing introspection",
115 qPrintable(propertyData.type), qPrintable(ifaceName),
116 qPrintable(propertyName));
119 const QString access = attributes.value(QLatin1String("access")).toString();
120 if (access == QLatin1String("read"))
121 propertyData.access = QDBusIntrospection::Property::Read;
122 else if (access == QLatin1String("write"))
123 propertyData.access = QDBusIntrospection::Property::Write;
124 else if (access == QLatin1String("readwrite"))
125 propertyData.access = QDBusIntrospection::Property::ReadWrite;
127 qDBusParserError("Invalid D-BUS property access '%s' found in property '%s.%s' while parsing introspection",
128 qPrintable(access), qPrintable(ifaceName),
129 qPrintable(propertyName));
130 return false; // invalid one!
133 while (xml.readNextStartElement()) {
134 if (xml.name() == QLatin1String("annotation")) {
135 parseAnnotation(xml, propertyData.annotations);
136 } else if (xml.prefix().isEmpty()) {
137 qDBusParserError() << "Unknown element" << xml.name() << "while checking for annotations";
139 xml.skipCurrentElement();
142 if (!xml.isEndElement() || xml.name() != QLatin1String("property")) {
143 qDBusParserError() << "Invalid property specification" << xml.tokenString() << xml.name();
150 static bool parseMethod(QXmlStreamReader &xml, QDBusIntrospection::Method &methodData,
151 const QString &ifaceName)
153 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("method"));
155 const QXmlStreamAttributes attributes = xml.attributes();
156 const QString methodName = attributes.value(QLatin1String("name")).toString();
157 if (!QDBusUtil::isValidMemberName(methodName)) {
158 qDBusParserError("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection",
159 qPrintable(methodName), qPrintable(ifaceName));
163 methodData.name = methodName;
165 QDBusIntrospection::Arguments outArguments;
166 QDBusIntrospection::Arguments inArguments;
167 QDBusIntrospection::Annotations annotations;
169 while (xml.readNextStartElement()) {
170 if (xml.name() == QLatin1String("annotation")) {
171 parseAnnotation(xml, annotations);
172 } else if (xml.name() == QLatin1String("arg")) {
173 const QXmlStreamAttributes attributes = xml.attributes();
174 const QString direction = attributes.value(QLatin1String("direction")).toString();
175 QDBusIntrospection::Argument argument;
176 if (!attributes.hasAttribute(QLatin1String("direction"))
177 || direction == QLatin1String("in")) {
178 parseArg(attributes, argument);
179 inArguments << argument;
180 } else if (direction == QLatin1String("out")) {
181 parseArg(attributes, argument);
182 outArguments << argument;
184 } else if (xml.prefix().isEmpty()) {
185 qDBusParserError() << "Unknown element" << xml.name() << "while checking for method arguments";
187 xml.skipCurrentElement();
190 methodData.inputArgs = inArguments;
191 methodData.outputArgs = outArguments;
192 methodData.annotations = annotations;
198 static bool parseSignal(QXmlStreamReader &xml, QDBusIntrospection::Signal &signalData,
199 const QString &ifaceName)
201 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("signal"));
203 const QXmlStreamAttributes attributes = xml.attributes();
204 const QString signalName = attributes.value(QLatin1String("name")).toString();
206 if (!QDBusUtil::isValidMemberName(signalName)) {
207 qDBusParserError("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection",
208 qPrintable(signalName), qPrintable(ifaceName));
212 signalData.name = signalName;
215 QDBusIntrospection::Arguments arguments;
216 QDBusIntrospection::Annotations annotations;
218 while (xml.readNextStartElement()) {
219 if (xml.name() == QLatin1String("annotation")) {
220 parseAnnotation(xml, annotations);
221 } else if (xml.name() == QLatin1String("arg")) {
222 const QXmlStreamAttributes attributes = xml.attributes();
223 QDBusIntrospection::Argument argument;
224 if (!attributes.hasAttribute(QLatin1String("direction")) ||
225 attributes.value(QLatin1String("direction")) == QLatin1String("out")) {
226 parseArg(attributes, argument);
227 arguments << argument;
230 qDBusParserError() << "Unknown element" << xml.name() << "while checking for signal arguments";
232 xml.skipCurrentElement();
235 signalData.outputArgs = arguments;
236 signalData.annotations = annotations;
241 static void readInterface(QXmlStreamReader &xml, QDBusIntrospection::Object *objData,
242 QDBusIntrospection::Interfaces *interfaces)
244 const QString ifaceName = xml.attributes().value(QLatin1String("name")).toString();
245 if (!QDBusUtil::isValidInterfaceName(ifaceName)) {
246 qDBusParserError("Invalid D-BUS interface name '%s' found while parsing introspection",
247 qPrintable(ifaceName));
251 objData->interfaces.append(ifaceName);
253 QDBusIntrospection::Interface *ifaceData = new QDBusIntrospection::Interface;
254 ifaceData->name = ifaceName;
256 while (xml.readNextStartElement()) {
257 if (xml.name() == QLatin1String("method")) {
258 QDBusIntrospection::Method methodData;
259 if (parseMethod(xml, methodData, ifaceName))
260 ifaceData->methods.insert(methodData.name, methodData);
261 } else if (xml.name() == QLatin1String("signal")) {
262 QDBusIntrospection::Signal signalData;
263 if (parseSignal(xml, signalData, ifaceName))
264 ifaceData->signals_.insert(signalData.name, signalData);
265 } else if (xml.name() == QLatin1String("property")) {
266 QDBusIntrospection::Property propertyData;
267 if (parseProperty(xml, propertyData, ifaceName))
268 ifaceData->properties.insert(propertyData.name, propertyData);
269 } else if (xml.name() == QLatin1String("annotation")) {
270 parseAnnotation(xml, ifaceData->annotations);
271 xml.skipCurrentElement(); // skip over annotation object
273 if (xml.prefix().isEmpty()) {
274 qDBusParserError() << "Unknown element while parsing interface" << xml.name();
276 xml.skipCurrentElement();
280 interfaces->insert(ifaceName, QSharedDataPointer<QDBusIntrospection::Interface>(ifaceData));
282 if (!xml.isEndElement() || xml.name() != QLatin1String("interface")) {
283 qDBusParserError() << "Invalid Interface specification";
287 static void readNode(const QXmlStreamReader &xml, QDBusIntrospection::Object *objData, int nodeLevel)
289 const QString objName = xml.attributes().value(QLatin1String("name")).toString();
290 const QString fullName = objData->path.endsWith(QLatin1Char('/'))
291 ? (objData->path + objName)
292 : QString(objData->path + QLatin1Char('/') + objName);
293 if (!QDBusUtil::isValidObjectPath(fullName)) {
294 qDBusParserError("Invalid D-BUS object path '%s' found while parsing introspection",
295 qPrintable(fullName));
300 objData->childObjects.append(objName);
303 QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path,
304 const QString& xmlData)
305 : m_service(service), m_path(path), m_object(new QDBusIntrospection::Object)
307 // qDBusParserError() << "parsing" << xmlData;
309 m_object->service = m_service;
310 m_object->path = m_path;
312 QXmlStreamReader xml(xmlData);
316 while (!xml.atEnd()) {
319 switch (xml.tokenType()) {
320 case QXmlStreamReader::StartElement:
321 if (xml.name() == QLatin1String("node")) {
322 readNode(xml, m_object, ++nodeLevel);
323 } else if (xml.name() == QLatin1String("interface")) {
324 readInterface(xml, m_object, &m_interfaces);
326 if (xml.prefix().isEmpty()) {
327 qDBusParserError() << "skipping unknown element" << xml.name();
329 xml.skipCurrentElement();
332 case QXmlStreamReader::EndElement:
333 if (xml.name() == QLatin1String("node")) {
336 qDBusParserError() << "Invalid Node declaration" << xml.name();
339 case QXmlStreamReader::StartDocument:
340 case QXmlStreamReader::EndDocument:
341 case QXmlStreamReader::DTD:
344 case QXmlStreamReader::Comment:
345 // ignore comments and processing instructions
348 qDBusParserError() << "unknown token" << xml.name() << xml.tokenString();
353 if (xml.hasError()) {
354 qDBusParserError() << "xml error" << xml.errorString() << "doc" << xmlData;